Vuex con TypeScript
Rodrigo Ariel Herera
Posted on May 22, 2020
Hola!, primero que nada darte las gracias por entrar al post, paso a presentarme de forma breve.
Mi nombre es Rodrigo y trabajo principalmente en Backend con Go y NodeJS.
Mi experiencia con todo el ecosistema de Frontend es relativamente nueva, pero está siendo más que satisfactoria experiencia :)
Vamos a comenzar a hablar del post.
Vuex con Typescript
Que es éste post y que no es.
- Es un explicativo de implementación funcional y fácil de entender de Vuex usando TypeScript.
- No es un paso a paso de un proyecto desde 0 con Vue, Vuex y TypeScript.
Tuve que hacer un proyecto fullstack en la facultad y elegí como framework de frontend a Vue ya que me pareció tener una curva de aprendizaje relativamente sencilla y viendo ejemplos creía poder llegar a algo lindo.
Probé hacer proyectos chicos usando Vue con JS plano y todo salió perfecto.
Ahora, en ocasiones me siento más cómodo usando TypeScript y resultó ser que Vue acepta usar TypeScript en lugar de JS, ese fue mi pie para darme el OK a usar Ts en mi proyecto.
A medida que fui haciendo, fui descubriendo que las cosas se hacían de distinta manera al regular de JS con Vue, pero todo salía sin inconveniente, hasta que... tuve que empezar a implementar Vuex.
Este fue el punto que más me costó de todo el proyecto, por todo los lugares donde buscará referencias, o se implementaba de maneras distintas o todos los artículos estaban incompletos.
Con esto último que acabo de decir, espero que quede claro la consigna del post, un ejemplo FUNCIONAL y que abarque todos los aspectos de la implementación de Vuex con TypeScript.
Empecemos!!!
Your Wall
Es el nombre que le di al proyecto, es algo super sencillo en donde:
- Se completa una descripción.
- Se carga una imagen.
- Se envia la imagen y la descripción a un backend.
- Luego el backend nos devuelve una lista de todas las imágenes que se han enviado.
Sencillo, ¿cierto?.
Lo que yo necesitaba era que, cada vez que mi aplicación cargue, traiga todos los posts del backend y cuando yo envíe uno nuevo, me actualice la lista ya existente en mi aplicativo.
La implementación que SÍ funciona
Para la implementación voy a mostrar ciertas líneas de código para que tengamos presente su funcionamiento y la relación entre Vuex y los componentes.
Vamos a mostrar 2 componentes los cuales son:
- AddPost.vue
- PostList.vue
Para vuex solo tenemos 1 store el cual es:
- Post.ts
Estructura:
your-wall
├── store
├── modules
├── components
Vamos a instalar una libreria llamada 'vuex-module-decorators'.
Dentro de la carpeta store vamos a crear el archivo index.ts, con el siguiente código:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex)
const store = new Vuex.Store({
state: {},
actions: {},
mutations: {},
});
export default store;
Lo vamos a utilizar para inicializar el store de vuex.
Dentro de store, creamos la carpeta modules con el archivo Post.ts dentro.
Vamos a importar las dependencias de 'vuex-module-decorators'.
import { VuexModule, Module, Mutation, Action, config, getModule} from 'vuex-module-decorators';
Luego importo la interfaz del Post y el store que inicializamos más arriba!
import { Post } from '../models';
import store from '@/store';
Definimos el Módulo:
config.rawError = true;
@Module({
name: 'posts',
namespaced: true,
dynamic: true,
store,
})
La primer línea del config.rawError es por si ocurre algún error con la librería, nos devuelva un error-dev-newbie-friendly y no una excepción de la librería.
Luego usando la notación @ definimos el nombre del módulo, le inyectamos el store y para esta forma de utilizar el módulo, tenemos que indicarle que es dinámico, ya que no lo estamos definiendo en el Store per se.
Teniendo esto hecho, vamos a proceder a definir la clase y los métodos del módulo!
La definición de la clase es la siguiente:
class PostStore extends VuexModule { }
Vamos a tener 1 estado, 1 get, 2 mutaciones y 2 acciones.
El estado es un array de la interfaz que importamos:
posts: Post[] = [];
El get nos va a retornar el estado:
get Posts() {
return this.posts;
}
Ahora, vamos a declarar las 2 mutaciones que luego van a ser triggereadas por las acciones.
Esta mutación recibe por parámetro un Post y lo agrega en la primer posición del array posts.
@Mutation
addPost(post: Post) {
this.posts.unshift(post);
}
Esta mutación recibe por parámetro un array de Posts y los agrega al estado posts.
@Mutation
setPosts(postParam: Post[]){
this.posts = postParam;
}
Ahora vamos con las acciones, vamos a tener 2 también!
Cuando definimos una acción, le tenemos qué indicar a que mutación va a afectar, en este caso va a mutar a 'addPost' que definimos arriba.
Es simplemente una función que recibe por parámetro un formdata que cuenta con 2 atributos, lo postea al backend y lo retorna a la mutación 'addPosts'.
@Action({commit: 'addPost'})
async addPostAct(data: FormData) {
const requestOptions = {
method: 'POST',
body: data,
}
const response = await fetch('http://localhost:8888/api/post', requestOptions);
if (response.ok) {
const res: Post = await response.json();
return res;
}
}
Esta acción busca los posts al backend y muta a 'setPosts' con la data que nos devolvió el backend.
@Action({commit: 'setPosts'})
async getPosts() {
const response = await fetch("http://localhost:8888/api/post");
if (response.ok) {
const res: Post[] = await response.json();
return res;
}
}
Y por finalizar, debajo de la definición de la clase vamos a exportar al módulo :D
export default getModule(PostStore);
BIEN, ya tenemos listo Vuex, vamos a implementarlo en los componentes!!
Vamos a empezar con el componente PostList.vue.
Importamos el módulo que acabamos de configurar:
import posts from '@/store/modules/Post';
En nuestra definición de clase, vamos a agregar que nos traiga todos los posts del backend y usaremos el estado que declaramos!
get posts() {
return posts.posts
}
Y usando el método created() del life-cycle de Vue, llamamos al store para traer los posts del backend!
async created() {
await posts.getPosts()
}
SUPER FÁCIL
Cuando usamos 'posts', estamos haciendo referencia al módulo que importamos del store.
Un ejemplo del template de ese componente puede ser el siguiente:
<template>
<div>
<div class="container">
<div v-bind:key="post.id" v-for="post in posts">
<PostItem v-bind:post="post"/>
</div>
</div>
</div>
</template>
Bien, éste componente ya está listo, ya está llamando al backend para que nos traiga los posts y cuando alteramos el estado, lo vamos a tener actualizado siempre!
Vamos al otro componente, AddPost.vue.
Mismo que el ejemplo anterior, importamos el módulo:
import posts from '@/store/modules/Post';
En este componente lo que vamos a utilizar es la acción de crear un nuevo post, que se vería algo cómo:
async addNewPost() {
const fd = new FormData();
fd.append('image', this.selectedFile, this.fileName);
fd.append('description', this.description);
await posts.addPostAct(fd);
}
Y ya está, eso es todo.
El ejemplo completo estará subido en mi GitHub junto con el backend que use.
Repositorio Front
Repositorio Back
Espero que les haya servido este ejemplo, yo verdaderamente estuve bastante horas leyendo, probando, re-leyendo, borrando, agregando y terminaba con un spaguetti code que era terrible.
Espero que tengan un excelente día y nos vemos la próxima!!
Posted on May 22, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.