Vue User Manager - Step 2

In the previous step of this tutorial, we quickly set up the files for our user manager app. In this step we'll create a component that displays a list of all the users in our data set. Make sure to put the following code after the Vue app is created, but before it is mounted. Note that in the previous step I placed a comment to mark the spot (if you aren't sure where to place the following code samples, scroll down to the bottom of the page, I've included the finished main.js file for your convenience:).

Here's the code for our UserList component:

// UserList component:
app.component("user-list", {
    props: {
        users: {
            type: Array,
            required: true
        }
    },
    template: `
        <div class="user-list">
            <h2>User List</h2>
            <ul>
                <li v-for="user in users" :key="user.id" @click="handleClick(user)">
                    {{ user.firstName + " " + user.lastName }}
                </li>
            </ul>
        </div>`,
    methods: {
        handleClick(user){
            this.$emit("user-selected", user);
        },
    }
});

The UserList must display our user data, and this data will be passed to it as a prop (from the parent component, which will be the rootComponent). Note the use of the v-for directive, it will loop through the user data and create an LI element for each user. When using a v-for, you should bind the key to a unique id (each user's id works well).

When an LI element is clicked, it will trigger the handleClick method, and pass in the associated user as a parameter. The body of the handleClick method emits a 'user-selected' event, and passes the selected user as the event object. We'll add code to the rootComponent that listens for this event in a minute.

Now that we've defined the UserList component, we can add it to the template of the root component. Put this in the template of the rootComponent, just under the button:

<user-list :users="users" @user-selected="handleUserSelected" />

This line of code is doing a few things:

  • It adds the user-list component to the root component
  • It passes the users array as a prop (the prop is also called users - this can be confusing for beginners)
  • It listens for 'user-selected' events coming from the UserList, which will trigger a handleUserSelected method, which we haven't yet defined (we'll do that in just a minute).

Now update the data method in the rootComponent to look like this (notice that I've added a data member called selectedUser and intializes it to null - don't forget to add the comma after userData):

data(){
    return {
        users: userData,
        selectedUser: null
    }
}

The selectedUser data member will allow the rootComponent to keep track of the user that has been selected from the UserList

Add this method to the rootComponent, after the addUser method (and don't forget to add the comma after the addUser method):

handleUserSelected(user){
    this.selectedUser = user  
    console.log("TODO: Show details for " + this.selectedUser.firstName);
}

Run the app now, and you should see a list of users appear. When you click on a user name, you should see a message in the console log that indeicates which user was selected from the list. This happens because the UserList component listens for clicks on LI elements, and then emits a 'user-selected' event and includes the selected user as the event object (the 'event object' is the second parameter passed into the $emit() method). The rootComponent listens for these events and receives the selected user as the event object.

We haven't yet run into the stumbling blocks that I mentioned in the previous step, we'll start to see them in the next step. But in this step we have covered two very important concepts in Vue:

  1. To send data from a parent component to a child component, use a prop. The root component sends the users array to the UserList component via the 'users' prop. This prop is defined in the UserList, and the data is bound to the prop in the <user-list> element, which we added to the template of the rootComponent.
  2. To send data from a child component to a parent component, emit an event and include the data as the 'event object' (the second parameter in the $emit() method). The UserList emits a 'user-selected' event and passes the selected user as the event object. When we added the user-list element to the template of the rootComponent, we also included the @user-selected attribute. This declared that the rootComponent should listen for 'user-selected' events, and trigger the handleUserSelected method when the event occurs.

At this point, your main.js file should look like this:

var userData = [
    {id:1, firstName:"Jane", lastName:"Doe", email:"jdoe@acme.com"},
    {id:2, firstName:"Tony", lastName:"Thompsom", email:"tony@acme.com"},
    {id:3, firstName:"Jesse", lastName:"Jones", email:"jesse@acme.com"}
];

const rootComponent = {
    template:   `<div>
                    <h1>User Manager</h1>
                    <p>Number of users: {{users.length}}</p>
                    <br>
                    <button @click="addUser">Add User</button>
                    <!--We'll add a few Vue components here later-->
                    <user-list :users="users" @user-selected="handleUserSelected" />
                </div>`,
    data(){
        return {
            users: userData,
            selectedUser: null
        }
    },
    methods:{
        addUser(){
            alert("TODO: Add User");
        },
        handleUserSelected(user){
            this.selectedUser = user  
            console.log("TODO: Show details for " + this.selectedUser.firstName);
        }
    }
};

const app = Vue.createApp(rootComponent);

// UserList component:
app.component("user-list", {
    props: {
        users: {
            type: Array,
            required: true
        }
    },
    template: `
        <div class="user-list">
            <h2>User List</h2>
            <ul>
                <li v-for="user in users" :key="user.id" @click="handleClick(user)">
                    {{ user.firstName + " " + user.lastName }}
                </li>
            </ul>
        </div>`,
    methods: {
        handleClick(user){
            this.$emit("user-selected", user);
        },
    }
});

app.mount('#app');

In the next step, we'll create a component that displays the details of the selected user. And we'll finally get into some of the pitfalls that I've been talking about.