Duda con el planteamiento de un MenuBar (Vue.js)

B

Buenos días gente de bien,

hace unos días decidí empezar un proyecto nuevo para familiarizarme con los frameworks de Spring Boot y Vue.js (PrimeVUE) respectivamente, ya que hasta ahora todo lo que había visto había sido vanilla tanto en Java como en Javascript y no hacen más que pedir frameworks por todas partes.

Bien, el caso es que la parte del backend la llevo bastante avanzada pero con el frontend al ser algo totalmente nuevo lo de los componentes y la sintaxis que se usa, me pierdo a menudo.

Estoy intentando seguir el patrón de Single Page Application y para ello lo he planteado con vistas y he usado la directiva router. Hasta ahí guay, funciona como debería. El problema viene con un MenuBar y sus respectivos MenuItems a los que quiero asociar un trigger para llamar a las funciones de los componentes en los que sean importados. Es decir, no sé si el planteamiento está mal de base o si me falta algo que no consigo ver.

<template>
    <Menubar :model="items" class="p-mb-2" />
</template>

<script>
export default {
    data() {
        return {
            items: [
                {
                    label: "Añadir",
                    icon: "pi pi-fw pi-plus",
                    command: () => this.create()
                },
                {
                    label: "Editar",
                    icon: "pi pi-fw pi-pencil",
                    command: () => this.edit()
                },
                {
                    label: "Borrar",
                    icon: "pi pi-fw pi-trash",
                    command: () => this.delete()
                }
            ]
        };
    }
};
</script>

Este sería el componente MenuBar que quiero cargar en 3 distintos componentes respectivamente según toque.

<template>
    <Panel header="Categorías" class="p-col-12 p-lg-6 p-mx-auto">
        <CrudMenuBar />

    <DataTable
        :value="categorias"
        :paginator="true"
        :rows="10"
        paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown"
        class="p-datatable-striped"
    >
        <Column
            v-for="col in columns"
            :field="col.field"
            :header="col.header"
            :key="col.field"
            sortable="true"
        />
    </DataTable>

    <Dialog
        v-model:visible="displayModal"
        :breakpoints="{ '960px': '75vw' }"
    >
        <template #header>
            <h3>Crear Categoría</h3>
        </template>

        <span class="p-float-label">
            <InputText
                id="inputNombre"
                type="text"
                v-model="categoria.nombre"
            />
            <label for="inputNombre">Nombre</label>
        </span>

        <template #footer>
            <Button
                label="Guardar"
                class="p-button-success"
                @click="save"
            />
            <Button
                label="Cancelar"
                class="p-button-warning"
                @click="cancel"
            />
        </template>
    </Dialog>
</Panel>
</template>

<script>
import CategoriaService from "../service/CategoriaService.js";
import CrudMenuBar from "./CrudMenuBar.vue";

export default {
    components: {
        CrudMenuBar
    },
    methods: {
        getAll() {
            this.categoriaService
                .getCategorias()
                .then((data) => (this.categorias = data.data));
        },
        create() {
            this.displayModal = true;
        },
        save() {
            this.categoriaService.save(this.categoria).then((data) => {
                if (data.status === 200) {
                    this.displayModal = false;
                    this.categoria = {
                        id: null,
                        nombre: null
                    };
                    this.getAll();
                }
            });
        },
        cancel() {
            this.displayModal = false;
        }
    },
    data() {
        return {
            categoria: {
                id: null,
                nombre: null
            },
            displayModal: false,
            categorias: null,
            columns: null
        };
    },
    categoriaService: null,
    created() {
        this.categoriaService = new CategoriaService();

    this.columns = [
        { field: "id", header: "Id" },
        { field: "nombre", header: "Nombre" }
    ];
},
mounted() {
    this.getAll();
}
};
</script>

Y este sería uno de los 3 componentes que lo van a importar. Pensaba que como al invocar dicho método el MenuBar ya iba a estar "montado" en el componente en cuestión, ese this.create() llamaría al create() del componente en el que estuviera alojado, pero al parecer no va así.

He leído sobre props y pasar parámetros de padre a hijo pero no sé si es lo que necesito. En Javascript a pelo esto lo hacía con un addEventListener() pero no estoy seguro de cómo hacerlo aquí.

Halp pls.

MisKo

Estoy en la cama recien despierto, pero no veo lo que dices.

Primero pones un codigo que usa el menubar, pero que no es el menubar y lo llamas menubar xD (¿es crudmenubar?)

Luego en éste pasas unas funciones que no están declaradas en el componente (create, edit, delete)

En cualquier caso, si quieres que un componente ejecute algo de otro tendrias que pasarselo por parametros o emitir eventos y configurarlos en el padre.

Edit: si me das 30 mins q sea persona lo veo mejor ajaaja, aunque seguramente baste con:

Opcion 1: Saltarte CrudMenuBar y poner ese codigo directamente en el segundo
Opcion 2: pasarle a crudmenubar las 3 funcs
Opcion 3: emitir eventos en CrudMenuBar

Personalmente, me gusta la 3.

spoiler

Donde lo vayas a usar

spoiler

EDIT2: No conocía PrimeVUE, lo miraré a ver que tal xD

1 respuesta
B

#2 Te comía el pene a riesgo de reducir el cupo por año, los $emit es lo que no sabía como usar.

Como ya habrás visto leyendo el edit el Menubar es como llama PrimeVUE al componente importado en main.js en sí, luego yo a ese componente lo llamaba CrudMenuBar para importarlo en CategoriaCrud, WhateverCrud, etc. para luego llevarlos a las vistas.

La opción 1 que comentabas de meter a pelo en el propio componente lo que estaba separando es lo que intentaba evitar viendo que se repetía en 3 cruds que básicamente llamaban a lo mismo, la segunda opción no sabría cómo hacerla... ¿con props? y la tercera es la que tenía en mi cabeza cuando lo planteé.

1 respuesta
MisKo

#3 La segunda opción sería algo así:

spoiler

Donde lo vayas a usar

spoiler

A mi me parece más limpia la opción 3, pero supongo que la 2 también es válida xD

1 respuesta
B

#4 Hm, sería un comportamiento similar a una interfaz, ¿no? creas una referencia a métodos que han de ser importados con props y luego esos métodos los implementas en el componente.

Gracias por la explicación, me ha quedado mucho más claro qué son los props y los $emits.

1 respuesta
MisKo

#5 Si, sería algo asi.

El tema es que al definir las props, puedes indicar el tipo de dato que esperas y si es necesario pasar la prop. En caso de que no se pase la prop o el tipo no sea válido, se emite un warn por consola pero nada más.

Esto hace que si estás esperando una función y te pasan un string, o un int, se queje por consola pero siga funcionando y, al darle al botón, intente ejecutar la funcion (en este caso, el int o el string) y pete de nuevo.

Para el tema de las funciones, prefiero emitir eventos en los componentes y escucharlos desde donde los use a pasar las funciones por referencia y tener luego errores dificiles de debuggear xD

1

Usuarios habituales