Day 3 of Vue.js

Emit

You can attach custom event listeners to elements to listen for events that are announced by the sub-elements. e.g.

<modal v-if="showModal" @close="showModal = false" body="test"></modal>

<script>
Vue.component('modal', {
    props: ['body'],
    template: `
        <div class="modal is-active" v-show="showModal">
            <div class="modal-background"></div>
            <div class="modal-content">
                <div class="box">
                {{ body }}
                </div>
            </div>
            <button class="modal-close" @click="$emit('close')"></button>
        </div>
    `
});
</script>

Notice that in the modal component’s template, the close button will make an announcement a ‘close’ event when it is clicked. The modal component’s custom event listener hears that announcement and change the showModal value to false, hence closing the modal.

Tabs

One downside of using plain html + vanilla javascript or jquery to implement tabs is that it tends to be very wordy. Typically you need to write the tabs class themselves then write another section that houses the actual stuff under each tab – so tab details and the tab are separated in your html code.
With Vue tabs can be written more elegantly.

<tabs>
            <tab name="About Us" :selected="true">
                <h1>Here is something about us</h1>
            </tab>
</tabs>

And in javascript:

Vue.component('tabs', {
    template: `
        <div>
            <div class="tabs">
                <ul>
                    <li v-for="tab in tabs" :class="{ 'is-active': tab.isActive }">
                        <a :href="tab.href" @click="selectTab(tab)">
                            {{ tab.name }}
                        </a>
                    </li>
                </ul>
            </div>

            <div class="tabs-details">
                <slot></slot>
            </div>
        </div>
    `,
    data() {
        return { tabs: [] };
    },

    created() {
        this.tabs = this.$children;
    },

    methods: {
        selectTab(selectedTab) {
            this.tabs.forEach(tab => {
                tab.isActive = (tab.name == selectedTab.name);
            })
        }
    }
});

Vue.component('tab', {
    template: `
        <div v-show="isActive"><slot></slot></div>
    `,

    props: {
        name: { required:true },
        selected: { default:false }
    },

    data() {
        return { isActive: false }
    },

    computed: {
        href() {
            return '#' + this.name.toLowerCase().replace(/ /g, '-');
        }
    },

    mounted() {
        this.isActive = this.selected;
    }
});

In tabs component a tabs [] array is initialized and populated with the ‘s children. Then tab component is generated for each item in the array.
Each tab is initialized with isActive set to false, and set to true if you bind selected as true in that tab element. Then the part :class... binds the is-active class to the element that is selected.
isActive is also used to determine whether to show the tab details in the tab component as well.

This is still quite confusing for me. Shall come back to re-visit again in the future.

So the confusing part for me was how the tab details went in to one place if they were written in each respective tags?
My guess is that the slot <div><slot></slot></div> in tab component eventually went into the slot in tabs component, so all the stuff written in all <tab>'something'</tab> all went into <div class="tab-details"> and got rendered as one div.

Leave a Reply

Your email address will not be published. Required fields are marked *