Vue User Manager - Part 2

Part 2 - A User List Component

In this part we'll create a component that displays a list of all the users in the database.

Update the main.js file by adding the following code, it should be placed after the app has been created, but before it has been mounted (there should be a comment in the code that indicates where to define components):

Here is the starter code for our user-list 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){
            console.log("LI clicked for this user:", user);
            this.$emit("user-selected", user);
        },
    }
});

First note that this code is calling the component() method of the Vue app object.

The component() method takes two very important parameters. The first parameter allows you to name your component (in this case 'user-list'). If you continue to poke around with Vue you may find that there are different approaches to naming your components, and referring to them by name.

The second parameter is an object that defines many things about the component, and it is known as the options object. We'll be talking about the options object on a daily basis as we continue our journey into Vue.

If you look closely at the options object, you'll notice it has a few properties in it (props, template, methods). Let's discuss each one of them.

In Vue, props allow you to pass data from a parent component to a child. The user-list component must display data for all the users in the database, an array of user objects will be passed to it as a prop from the parent component. In a moment we will add an instance of this component to the root component. So the root component will be the parent, and will pass it's 'users' data member as a prop to the user-list component. We'll talk much more about props in the near future, and there are different ways to declare the props for a component. For now, note that the user-list component defines a prop called 'users', which will be an array and is required in order for this component to work properly.

The template property of the options object allows you to define the UI (user interface) for your component. You should recognize that the template code looks a lot like HTML, but with some strange attributes These strange attributes are known as directives in Vue.

The v-for directive will instruct Vue to loop through the 'users' prop and create an LI element for each user. When looping through arrays with a v-for directive, you should also add a :key attribute and bind it to a unique id (each user's id works well). In Vue, when you use a v-for loop you should always add a key attribute and bind it to a unique id. This allows Vue to keep track of all the elements that are added to the DOM in the loop.

The @click attribute is Vue's way of allowing you to add a click event handler to a component. We'll also be learning more about events in Vue as we move through the project. In this case, each LI element will trigger the handleClick() method and pass the associated (current) user as a parameter.

The body of the handleClick() method emits a 'user-selected' event, and passes the selected user as the event object. Note that the $emit() method is a built-in one that you can make use of in any Vue component that you create.

The $emit() method is a very important one because it is Vue's way of passing data from a child component to a parent. The first parameter you pass provides a name for the event that your component is emitting to it's listeners. The second parameter is often called the 'payload' because it is the data that is being passed from the child component to the parent (assuming that the parent is set up to listen for 'user-selected' events).

A very important concept in Vue is understand that props are used to pass data from a parent component to a child component. And that the $emit() method is used to pass information from a child component to a parent. In order for this to work in our scenario, the parent must listen for a 'user-selected' event. We'll do this in the next step when we add the user-list component to the root component.

Now we'll configure the rootComponent to make use of the user-list component.

We can can add the user-list component to the template of the root component like so: (put this just under the BUTTON element in the template of the rootComponent, you should see a comment that marks the spot):

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

At this point you should be able to run the app by launching the index.html file in a browser. Note that a list of users should appear inside the root component, but if you click on an LI element, Vue will write a warning message to the console log because we have not defined the handleUserSelected() method in the root component.

By adding what appears to be a user-list element to our HTML code, we have added an instance of the user-list component to the app.

What appears to be something like two HTML attributes are actually part of the Vue framework. The :users prop (which was defined in the user-list component) is initialized to the users data member in the root component. This code also configures the rootComponent to listen for'user-selected' events, which may be emitted by the user-list component. When a 'user-selected' event is emitted, the root component will user the handleUserSelected() method as the event handler. We have not yet added this method to the root component, but we will in just a moment.

But first, add this data member to the rootComponent (add it to the object that is returned by the data() method of the rootComponent, don't forget to put a comma before the preceding line since you are adding a property to an object):

selectedUserId: null

The selectedUserId data member that we've just added will allow the rootComponent to keep track of the user that has been selected from the user-list component.

Now add this method to the rootComponent (put it inside the methods object and after the add() method):

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

Run the app now (refresh index.html). When you click on a user name in the list, you should see a few messages in the console log which displays information for the user that was clicked on in the list. Note that the first message is generated in the user-list component. Then the 'user-selected' event is emitted. The root component is set up to listen for 'user-selected' events and triggers the handleUserSelected() method, which logs the second message to the console.

In summary, the user-list component may emit an event (a 'user-selected' event) if and when an LI is clicked. And the root component is set up to invoke the handleUserSelected() method when it detects the event.

It's important to remember that in Vue props are used to send information from a parent component to child component, and to send data from a child component to a parent component, the child should emit an event that often includes a payload).

In the next step, we'll create a component that displays the details of the user that is selected in the user-list.