4.3 The User List Form Control
In this lesson, we'll build some custom form functionality: a user list that will allow us to select which users can participate in the new project. Even though there's no "built-in" functionality for this, we can still use the features of Angular to create it.
1.Introduction5 lessons, 29:28
2.The Projects List6 lessons, 39:35
3.The Users5 lessons, 31:28
4.The New Project Form4 lessons, 30:48
5.The Project Page4 lessons, 49:46
6.Conclusion2 lessons, 04:08
4.3 The User List Form Control
Right now, our project form has two fields, the Name field and the Description field. What we would like to add is a list of checkboxes with user names so that you can choose who you would like to include in this project. Of course, a list of checkboxes like this is not quite as simple as the text-based fields that we have already included, so we're going to have to build this from scratch. So let's see how we can do this in Angular 2. Of course, to do this, we're going to need a list of the users of the system as well as the name of the currently logged in user. So this means we're going to need both of our services. So up at the top here, let's go ahead and import the AuthService, and we will also import the ProjectsService. Let's add both of these to the constructor. Now we're going to need a couple of different lists of users here. First of all, we're going to need a list of all the users in the system so we can display our list of checkboxes. So up here as a property on Site Users and this is just going to be an array of objects for now. And then down here on ngOnInit, let's add a user's property to our form group. And this is just going to be a new FormControl, and it's going to have a default value of an array. Now when we are creating strings here for name and description, basically we're creating a new FormControl with the default value of an empty string. But I figured I'd show you the alternate way to do it which is to actually write new FormControl, and we'll just start this off as an empty array. Now we don't actually have the FormControl class here. So let's jump up to our top here and we will Include FormControl in there. So we have our list of users that are part of this project, and initially, that's an empty array, that's good. We also have a list of users that is going to be all of the users to display as checkboxes, and that is currently not even instantiated. So what we need to do is get a list of all of the users of the system. Well, we have done this before, so we know we have the function for it. So here we're going to do this.service for our project service, .getusers, and then as you know, this returns an observable. So we will subscribe to this observable which will return us a list of users. Now in here, we need to figure out what the name of the user who is currently logged in is because we don't wanna show them their own name in the list because they're just gonna be included in this project by default since they are the ones creating this project. So in here, we'll say this.Auth.currentUser and we will subscribe to that. And we will get the currentUser. Now the first thing to do is to include this currentUser in the FormControl or in our users list here in our form group. So let's start by just saying let, I'll say currentUsername = currentUser.username. And then we can say this.form.controls['users'] and then we can say .setValue. And the set value function is the way we can set the value of the FormControl. And we will just set it to an array with the currentUsername in that array. So now our user FormControl has a value of an array with just the name of the currently logged in user. Now we also have this array of users up here on line 15. This array is supposed to be a list of all of the users who need checkboxes. So let's set this.users = our list of users that we got from our get users observable. We'll say users.filter, and for every user, we only want the one where the username is not equal to the currentUsername. So we're gonna have a list of everybody except the logged in user. So now we have a list of users that we can display as checkboxes. So let's hop over to our form here, and right underneath the paragraph for our text area. Let's include a div here and we'll put in a label with the text Users and then underneath this, let's have an unordered list. Inside this we'll have a list item, and of course, this is where we will have our *ngfor, and we'll say let user of users, and we will loop over and create a list item for every single one of these users. And then in here, let's include a label to wrap our checkbox. And of course, instead of using an input, we will use the nd-checkbox element. The text for this will be user.username. And of course, nd-checkbox will need a couple of properties. Let's add two. First of all, we need to know is this checkbox checked or not? And so for that, let's bind a value to the checked property of the checkbox. Remember, since we're binding to a property on the checkbox we use the square brackets. And to figure this out, let's call a function called isSelected. And we will pass that user.username. And so if this function returns true and checked will be true, and the box will show us checked. If it's false the box will show as unchecked. But then we also need to know what do we do when the box value changes or when the box is checked or unchecked? And so let's handle the change function. And when the change function happens, we will call onSelected. Now onSelected will take $event. Now we haven't looked at this object yet. But basically $event is the event object for the event that's happening here. Because we actually call the function here in Angular and don't actually just pass a reference to the function, if we want that events event object, we need to pass $event as a property to this function. However, that's not all we want. We also want to pass user.username as a second property to our onSelected function. So that should be all we need for our form. So now we have to come back to our component here, and we have to write these two functions. Let's see, what were they called? Let's start with isSelected. So isSelected. Of course, this takes a username which is a string, and it returns a boolean. And what we need to do in here is figure out if the username that was passed in is already in the list that we have in our form group here. Basically is it in this array inside of our FormControl object? So we can basically just look to see what the index of that value in the array is, and if it's -1 then we know it's not in the array. So let's write another helper function here called getIndex. It too will take the user name as a string but it's going to return a number. And what we can do in here is say this.form.controls ['users'].value. And this is how we can get the value that is inside of our FormControl. Notice that up here we used set value to set that value, and then we can just use the value property to get it out. So that gives us a reference to our array. So we can say, value.indexOf, and we will pass in that username. And that will return the index. So now that we have a function that gets that index in here, we can just return this.getIndex(username) > -1. If this is -1, of course, this will return false which means it's not in the array. So now we have isSelected, but was our other one? onSelected, right. So we have onSelected, this will take our event object as well as the username. Now in here it's our job to decide whether we have to add this username to the array inside of our FormControl or whether we have to remove this name from the array. Now we can figure out if this event was a checking or an unchecking event by using event.checked. If this value is true, we know that the user has checked the box. If it's false, we know that they have unchecked the box. So let's start by getting a couple of values here. Let's get a reference to the array by saying let users = this.form.controls [' users']. Let's get a reference to the list that is currently in that object, and we'll say newUsers is going to be users.value. And of course, at this point, it isn't the new list of users, but we are going to modify this list and then we will save that back to our user's FormControl. Finally, let's get the index by using this.get index, and we will pass that the username. Okay, so there are two scenarios that we want to look at here. If event is checked, meaning the user just checked that checkbox, and the index of that username is equal to -1, then we know that the name is not in the list, but that they have checked the box, meaning the name should be in the list. This is the simplest case. So we can overwrite our list of users here by saying newUsers = users.value .concat and we will concat an array with that new username. The reason I'm using concat instead of push here is because concat returns a new array instead of modifying an existing one. So we're just trying to be immutable in that way. Now you might wonder why are we overwriting the value of newUsers that we already set, and the reason we're doing that is, if for some reason our if blocks here aren't actually true, and so their contents is never run, we will have a value for new users here. And it will just basically reassign existing value and everything will be okay. So that's what to do if event was checked and the element was not in the list. If event was not checked, so we'll say, not event.checked, and the index is greater than -1. And what this means is, if event is not checked, which means they just unchecked the checkbox, and the element is in that list, well, then we need to remove it. So we'll newUsers = users.value.slice and we will slice from the beginning of the list all the way up into the index. And then we want to concat that with another array. The other part of the array is whatever is after the index. So we'll say, users.value.slice and we're going to slice from index+1. And that means we're basically slicing from one element after that index to the end. And let's actually put that on one line. All right, now really there should be no case other than these two if cases, but I guess if there's a bug or something and somehow they're unchecking a checkbox for a name that is not in the list, or they are checking a checkbox for a name that is already in the list. Then neither of these will run, and instead we will just reassign what is already in there. And everything should be just fine. Okay, so at the end of all this, we have a new users list which is the list of users we want. And so at the end here, we'll just say users.setvalue and we will set that to new users. Okay, so this should all be working now. So let's see it in action. If we try and view the form, you can see that we do have an exception here. No provider for project service. That's right, if we come here to our decorator, we forgot to include a provider's property. So I'll just include our project service there. Okay, we can come back to the form and notice what we have going on here. So we're signing as Jimmy, which means we should only see the other three users of our system showing up. So if I click Save right now, right, we have our validation. So I will include a project name. If I click Save Now, notice what the value of our form currently is. We have a description and the name, of course, but we also have an array of users. And right now we just have one user, Jimmy, which is ourselves cuz we are creating this project. So if I check Eddie's name and let's view this list again, you can see we have Jimmy, and we have undefined. Okay, that's interesting. Let's see if we can figure out why that is happening. Of course, the value is added into the array in onSelected here. And it's the username that's passed in. So if we look to where onSelect is called, okay, we're just calling user.name instead of user.username. So if we make that change, and let's let our form refresh, and now we'll go ahead and we'll select Eddie and click Save. And now array is Jimmy and Eddie. I can select April and Linn. We have four users in the array and it's all four users. Let's see what happens if I uncheck a user and save that. We have an array of three, Jimmy, Eddie, and Linn, excellent. All right, so now our form is finished. This is the object that we want to return to the server when asking it to create a new form. So that means in the next lesson we can focus on what happens during form submission.