Sunday, February 14, 2021

Vue.js - Conditional rendering and List

Rendering conditional Content


We will render the view based on some conditions to support 'reactive' looks and feels.


v-if, v-else-if, v-else


Using 'v-if' directive to render a block conditionally.

Note that, 'v-else' directive needs to follow 'v-if' or 'v-else-if' immediately.

Ex:

// index.html


    <section id="app">
        <button v-on:click="toggleMessage">Show/hide message</button>
        <p v-if="showMessage">Hello World!</p>
    </section>


// app.js


    const app = Vue.createApp({
        data() {
            return {
                showMessage: false,
            };
        },
        methods: {
            toggleMessage() {
                this.showMessage = !this.showMessage;
            },
        },
    });

    app.mount("#app");


If you inspect the DOM element, you will notice that if it is falsy condition, that html element will be removed from DOM.


v-show


Use 'v-show' will have the same outcome in screen, but the html element will be kept in DOM, and vue will only use CSS styling 'display:none' to hide it.

Ex:

// index.html

    <section id="app">
        <button v-on:click="toggleMessage">Show/hide message</button>
        <p v-show="showMessage">Hello World!</p>
    </section>


// app.js

    const app = Vue.createApp({
        data() {
            return {
                showMessage: false,
            };
        },
        methods: {
            toggleMessage() {
                this.showMessage = !this.showMessage;
            },
        },
    });

    app.mount("#app");



v-if vs v-show


Keep adding or removing element in DOM will be costly.

Also, we try not keep lots of unnecessary element in DOM even they are hidden.

So the principle is that if elements will show/hide frequently, then we use 'v-show'. Otherwise, we can just stick with 'v-if'.


List


We can use 'v-for' directive to render a list.

It also provide a format to track index of list.

Ex:

// index.html

    <section id="app">
        <input v-model="taskToAdd">
        <button v-on:click="addTask">Add</button>
        <ul>
            <li v-for="(taskindex) in tasks">{{ task }} - {{ index }}</li>
        </ul>
    </section>


// app.js
 
    const app = Vue.createApp({
        data() {
            return {
                taskToAdd: "",
                tasks: [],
            };
        },
        methods: {
            addTask() {
                this.tasks.push(this.taskToAdd);
                this.taskToAdd = "";
            },
        },
    });

    app.mount("#app");



Encountered issues when using v-for? (Refer to this) Vue has its own way to make rendering efficient (in-place patch strategy).
But there is an issue if the output rely on child component or temporary DOM state.
Take below as an example.

// index.html

    <section id="app">
        <input v-model="taskToAdd">
        <button v-on:click="addTask">Add</button>
        <ul>
            <li v-for="(task, index) in tasks">
                <p v-on:click="removeTask(index)">
                    {{ task }} - {{ index }}
                </p>
                <input v-on:click.stop>
            </li>
        </ul>
    </section>


// app.js

    const app = Vue.createApp({
        data() {
            return {
                taskToAdd: "",
                tasks: [],
            };
        },
        methods: {
            addTask() {
                this.tasks.push(this.taskToAdd);
                this.taskToAdd = "";
            },
            removeTask(index) {
                this.tasks.splice(index, 1);
            },
        },
    });

    app.mount("#app");


NOTE:

If we add two tasks A and B, and type something in the input box under task B section, once we remove the task A, you will see the text in input box under task B is removed. The reason is that in order to update the view efficiently, Vue will reuse the element and copy the task B to task A, and remove task B HTML element. But the input text of task B is the temporary DOM state and cannot be detect and move it to the position of task A .

In order to let Vue to know the identity of each item in list, we can use special 'key' attribute.
Also, we need to make key unique.

// index.html

    <section id="app">
        <input v-model="taskToAdd">
        <button v-on:click="addTask">Add</button>
        <ul>
            <li
                v-for="(task, index) in tasks"
                v-bind:key="task"
            >
                <p v-on:click="removeTask(index)">
                    {{ task }} - {{ index }}
                </p>
                <input v-on:click.stop>
            </li>
        </ul>
    </section>


// app.js

    const app = Vue.createApp({
        data() {
            return {
                taskToAdd: "",
                tasks: [],
            };
        },
        methods: {
            addTask() {
                this.tasks.push(this.taskToAdd);
                this.taskToAdd = "";
            },
            removeTask(index) {
                this.tasks.splice(index, 1);
            },
        },
    });

    app.mount("#app");


No comments:

Post a Comment