S
S
Sergey Krivosheev2018-10-09 22:51:36
Vue.js
Sergey Krivosheev, 2018-10-09 22:51:36

How to pass data between two components in Vue?

Good afternoon.
I understand with Vue and I want to make my own component that will allow me to make a universal mechanism for displaying forms and editing / adding values ​​​​in the table (and any other forms). Briefly, the component looks like this:
template.vue

<pages :currentPage.sync="currentPage" >
        <page name="index" title='Список проектов'>
            <template slot="toolbar">
                <v-btn color="primary" dark class="mb-2" @click="newProject">Новый проект</v-btn>
            </template>
            <v-data-table
                :headers="headers"
                :items="projects"
                hide-actions
                class="elevation-1"
            >
                ....
            </v-data-table>
        </page>
        <page parent="index" name="edit" title='Редактирование'>
....
</page>
</pages>

pages.vue
<template>
    <v-card width="100%" class="flexcard">
        <v-card-title primary-title>
            <v-breadcrumbs divider="/" class="pa-0">
                ....
            </v-breadcrumbs>
            <v-spacer></v-spacer>
            <v-card ref="toolbar">
               ......тут надо вставить slot toolbar из дочернего компонента page
            </v-card>
        </v-card-title>
        <v-card-text class="grow">
            <slot></slot>
        </v-card-text>
        <v-card-actions>
            <v-spacer></v-spacer>
        </v-card-actions>
    </v-card>
</template>

page.vue
<template>
    <div>
        <div v-if="name==pagesActive">
            <slot name="toolbar"></slot>
            <slot></slot>
        </div>
    </div>
</template>

<script>
    export default {
        data: () => ({

        }),
        computed:{
            pagesActive () {
                return this.$parent.$parent.activePage
            }
        },

        created () {
..вот тут хотелось бы получить доступ к родительскому компоненту и его слоту
            // this.$parent.$refs.toolbar = '123123'
        }
    }
</script>

I have already read a bunch of articles that passing from parent to child is very difficult. At the same time, I need to pass not just values, but preferably the entire slot='toolbar' from page.vue to the parent element in pages.vue.
Yes, I can get to this element from page.vue this.$parent.$children.find .... but more of a question of how to do it right. Is it possible to organize my logic or is it overkill and everything needs to be constantly copied from page to page.
What I want to end up with:
A universal component that allows you to build several pages on one route. Those. there will be several pages: a list of projects, editing / adding a project, editing additional project parameters. I want it all to be done beautifully through breadcrumbs, etc. (now, in principle, everything has already been implemented), but I want to use the space of the parent component pages.vue to insert controls there for a specific page.
Why do I need it. I want to limit myself from writing standard code page layout, header, breadcrumbs, controls in the header and in the footer of the form, single variables (because adding / editing and displaying a table is essentially one form, but I want to make it beautiful)

Answer the question

In order to leave comments, you need to log in

1 answer(s)
S
Sergey Krivosheev, 2018-10-12
@Nemozar

If anyone is interested, I implemented this task. All executable code and template had to be moved to the Page.vue component
Here is the code listing:
Pages.vue

<template>
    <div style="width: 100%; height: 100%">
        <slot></slot>
    </div>
</template>

<style>
    .flexcard {
        display: flex;
        flex-direction: column;
    }
</style>

<script>
    export default {
        data: () => ({
            pages : []
        }),
        mounted () {
            this.initPages()
        },
        methods: {
            initPages() {
                this.$children.forEach((child) => {
                    if (child.$options.name == 'Page'){
                        this.pages.push(child)
                    }
                })
            },
        }
    }
</script>

<template>
    <v-card width="100%" class="flexcard" v-if="name==activePage" style="height: 100%">
        <v-card-title primary-title style="padding-bottom: 0; padding-top: 0;">
            <slot name="breadcrumbs">
                <v-breadcrumbs divider="/" class="">
                    <v-breadcrumbs-item
                        v-for="item in breadcrumbs"
                        :key="item.text"
                        :disabled="item.disabled"
                        @click.native="activePage=item.page"
                        class="pa-0"
                    >
                        {{ item.text }}
                    </v-breadcrumbs-item>
                </v-breadcrumbs>
            </slot>
            <v-spacer></v-spacer>
            <slot name="toolbar"></slot>
        </v-card-title>
        <v-card-text class="grow">
            <slot></slot>
        </v-card-text>
        <v-card-actions>
            <v-spacer></v-spacer>
            <slot name="actions"></slot>
        </v-card-actions>
    </v-card>
</template>

<script>
    export default {
        data: () => ({

        }),
        computed:{
            name(){
                return this.$attrs.name
            },
            activePage : {
                get: function () {
                    return this.$store.state.activePage
                },
                set: function (newValue) {
                    this.$store.dispatch('setActivePage', newValue)
                }
            },
            breadcrumbs(){
                if (this.activePage) {
                    console.log(this.activePage)
                    let breadcr = this.getBreadcrumb(this.activePage).reverse()
                    return breadcr
                }else{
                    return []
                }
            },
        },
        methods:{
            getBreadcrumb(pageName) {
                let items = []
                let page = this.getPageByName(pageName)
                if (page) {
                    items.push({
                        text: page.$attrs.title,
                        page: page.$attrs.name
                    })
                    if (page.$attrs.parent) {
                        let parentItem = this.getBreadcrumb(page.$attrs.parent)
                        items = items.concat(parentItem)
                    }
                }
                return items
            },
            getPageByName(name){
                if (this.$parent.pages && this.$parent.pages.length > 0){
                    let page = this.$parent.pages.find((p) => {
                        return p.$attrs.name == name
                    })
                    return page
                }
                return null
            },
        },
    }
</script>

In any file we use the construction
<template>
    <pages style="height : 100%">
        <vue-element-loading :active="loading" spinner="bar-fade-scale"/>
        <page name="index" title='Список проектов'>
            <template slot="toolbar">
                <v-btn color="primary" small dark class="mb-2" @click.native="newProject">Новый проект</v-btn>
            </template>
            <v-data-table
                :headers="headers"
                :items="projects"
                hide-actions
                class="elevation-1"
            >
                <template slot="items" slot-scope="props" :disabled="true">
                    <td>{{ props.item.name }}</td>
                    <td class="justify-center layout px-0">
                        <v-btn flat icon small  @click="editItem(props.item)">
                            <v-icon
                                small

                            >
                                edit
                            </v-icon>
                        </v-btn>

                        <v-btn flat icon small color="error"
                               @click="deleteItem(props.item)"
                               :loading="props.item.loading"
                        >
                            <v-icon small>
                                delete
                            </v-icon>
                        </v-btn>
                    </td>
                </template>
            </v-data-table>
        </page>
        <page parent="index" name="edit" title='Редактирование'>
            <v-form v-model="valid">
                <v-layout wrap>
                    <v-flex xs12>
                        <v-text-field name="name" :error-messages="errors.collect('name')" :rules="nameRules" required v-model="editedItem.name" label="Название"></v-text-field>
                    </v-flex>
                    <v-flex xs12>
                        <v-spacer></v-spacer>
                    </v-flex>
                </v-layout>
            </v-form>
            <template slot="actions">
                <v-btn small @click.native="close">Отмена</v-btn>
                <v-btn
                    small
                    :disabled="!valid"
                    :loading="waitSave"
                    @click.native="save"
                >
                    Сохранить
                </v-btn>
            </template>
        </page>
    </pages>
</template>

Breadcrumbs of any level can be made by chaining parent links in the page component

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question