FREELessons: 17Length: 2.7 hours

Next lesson playing in 5 seconds

  • Overview
  • Transcript

1.7 Displaying the Contacts List

In this screen cast, we wanna take the work that we did in the previous two screen casts and bring those things together to display a list of contacts to our user. You'll recall that two screen casts ago, we set up our routing configuration. And as an example route, we created the /contacts route, which displayed the list template and used the list controller to display a page which will eventually show the users that list of contacts. Now, we didn't have a way to get contacts from the server, so in the previous screen cast we created our contacts resource, which talks to the rest API on our server and allows us to access the contact data. So, in this screen cast we're going to fill in that list controller and the list template to make that displaying of data actually happen. Now currently we don't actually have any data and we don't have a form yet that we can create to use a new contact, so we're going to have to go ahead and hard code some of that data in. You may recall from the previous screencast that we called our database data.json which means this is going to be the file that it will look to for actually finding the data. So I'm going to go ahead and write some JSON in here. So, this will be an array and then why don't we just make two contacts here. We need to have an id property. We also need a userId, then we're gonna have a firstName property. And each one of the properties of our contact records are going to be arrays and they're gonna have two values in them. The first one is the actual value, so for example, Jack. And the second one is going to be the type of the value. So for example, text. And the reason we do this is for validation purposes, because as you know, you can have many different input types in the browser. Traditionally its just been text input, but more recently with HTML5 input types we can have numbers, and dates, and even colors and things like that. And so, to take advantage of that feature, we have to give each field a type, and let's also have last name, and we'll set this to Rackum, and this will be text as well. There we go. So we have three values here and hopefully by the end of this screen cast we'll be displaying these three users in the browser. So the next thing to do is open up the public/source/controllers.js file. Now previously we created this list controller and now we need to go ahead and fill in some values. So the first thing we need to be able to do is to get those contacts from the server. To do this we use the contact factory that we created last time. So add that as a parameter here. And we'll replace this array, this empty array that we have right now is called contacts. And we'll replace this with contact.query. And this will go ahead and query the server and get all of the available contacts. Now remember, on the server side, we are making sure we only return the contacts of the user that is currently logged in, so we don't have to worry about filtering those at all here on the front end. We can just do contact.query. Now another thing we wanna do in this table is choose which fields should be displayed. So we'll have scope.fields and this is going to be an array. And right now, we'll just put firstName and lastName. And later on, we'll look at how we can allow the users to choose exactly what fields they'd like to be displayed. But for now, we'll just show the first name and the last name. Now, another thing we want to allow our users to do is sort the table based on the different columns. So, let's go ahead and add a scope.sort function. This function will take a field name and, it will go ahead and set scope.sort.field equal to whatever field is being passed in. And then we'll also do scope.sort.order and this will just be !$scope.sort.order and then down here we will set scope.sort.field to a default of firstName and scope.sort.order. Now, what exactly is this code doing? Well, first of all, we set scope.sort equal to this function which will be called when the user clicks on any one of the headings in the table and it will go ahead and re-sort the rows by that column. We're keeping track of what column is being sorted by adding a property to our function. And you can do this in JavaScript because a function is just another type of object, so we can add a property to it and the property that we're adding is called field and it will be whatever field they clicked on. We also changed the order, our sorting order will be done by boolean, false means to sort in descending order, every time this sort function is called it will swap this to the alternate boolean. So the first time we click a field, this will switch to true and the records will be sorted descending from zed to a. Now believe it or not, this is everything that we need to do in the controller and that's a good thing because controllers in Angular should really be as thin and as a light as possible. Really, their only job is to pass these different values into the scope that will be used to render the templates. Speaking of templates, let's go ahead and fix up our template that's in public/views/list.html and right now that is just a table however, we're going to change that up a bit. First thing I want to do is add an input value above this. This will be an input value of type search, and this will allow us to filter through the table. So let's say we're searching for a specific person or some specific email address, we can go ahead and type that into this search here. And our table rows will be filtered. Now we're going to give this the Bootstrap class of form-control, we'll give it a placeholder value of a search, and we'll use the Angular directive ng-model and we'll set that model equal to query. Now you should know, when we set ng-model equal to query here, that means whatever is typed into this input box will be set to scope.query. Basically if we wanted to have a default value there we could do scope.query and we could set it to something in here. However, we want that to be empty by default so we can just leave that off and this value will be changed every time we type a character into this input box. Okay, so now let's create our table. So we'll start by creating a table head element and in here we're gonna have our initial table row. And inside of our table row we'll have a table header element and the table header element will need to be repeated for each one of the fields that we wanna show. So each one of the fields in our scope.fields array. So to do this we will use the ng-repeat directive and we can say for each field in fields and the first thing we'll do is inside this we'll put the field name. When this table header is clicked, we want to run that sort function, right? So we have to wire up that event handle and we do that by doing ng-click. This is another directive that will go ahead and call the sort function and we wanna pass it the field and this field variable here is this field variable here. Oh, looks like I accidentally put first instead of field. So this is gonna be for each field in fields we will create a new table header element and we'll use that field variable in two places. First we'll actually display it to the user here, but also when this table header element is clicked on, we will sort by that field. Now when we sort by something, it would be helpful if we had a little arrow or chevron in this table header element to let the user know which direction it's currently being sorted. So we can do this with a span, and use the Bootstrap glyph icons. We'll add a class here and I'm going to paste this class name in because it's a little bit long. We need to have two class names here. The first one is a class of glyphicon. And the second one is a class for the specific icon we want so glyphicon-chevron-down. I going to copy this and paste it below and we're also gonna have glyphicon-chevron-up. So one, the down chevron will be displayed when we're sorting down. The up chevron will be displayed when we're sorting up. But right now, both of those chevrons are being displayed. So, what we need to do right now is add the ng-show directive. And the ng-show directive takes a JavaScript expression that should refer to Boolean value and if it's true, this element will be displayed, and if it's false, this element will be hidden. So what we wanna do is say if sort.field triple equals the field, and remember that field references whichever field this is the table header element for. So, if sort.field equals that field, then we know we're sorting by this field. And if !sort.order, because remember sort order will be false and we're sorting down from a to zed. So we have to say !sort.order. And if this expression here returns true, then we know that we are sorting down and so we'll show the chevron down. Now I'm going to copy that and paste it in here, and all we need to change is, take away the exclamation point. Because we only want to show this if this is the field being sorted by, and this time we are sorting up if sort order is true. So that's all we need to do for our table head element. And we should actually be able to see this in practice already. So let's head over to local host port 3000 and then let's head to port 3000/contacts and as you can see, we can see our input element right here which we can type in to but we don't have our table yet and we do have an error. And the error here says we have an unknown provider, contact provider. And if you think about it for a second, this should actually make sense because here in controller we are looking for a contact factory or in a lower level, a contact provider. However that contact provider was in a new file, public/source/factories.js, but we never actually added that to our main.html file. So right here at the bottom, I'm gonna copy our script to source/controllers. And I'm gonna replace controllers there with factories. And now, if we come to the browser, you refresh the page. You can see we have our table here, and currently there's only a table header. There's one little issue here. Notice the glyph icon is being displayed as a box instead of as the actual shape. And honestly, it took me a second to figure out exactly what was going wrong here, but I think the quick solution is to basically not just include the themed bootstrap that we have here, but we also need to include the standard bootstrap. So, this is at lib/bootstrap/dist/css/bootstrap.min.css. And if you include that and now we refresh the page, you can see we have our proper glyph icon showing up there. And the nice thing is if we go ahead and click this, you can see it changes up and down and if I click over here on lastName you can see now lastName has the Chevron going up and down. So that's a pretty good start to our template here. We successfully have done the table head, now let's move on to the table body. So let's go ahead and create a, tbody element here, and in here we want to create a table row. Now the table row, of course, is going to need to repeated for every single one of our contacts. Now in controllers.js our contacts are stored in an array called contacts. And so let's go ahead and say ng-repeat for every contact in contacts. But what we also want to do is use the pipe here and we're gonna tell Angular that we wanna filter this by the query. Now this query here refers to the query in our current scope which is the same model as the query that we have up here. So this is how we filter whatever contacts are being showed in our table. We only show contacts that match the query which is by default nothing or as we start typing the list of contacts will be filtered to match this query. The other thing we need to do is pipe again and use another filter, the orderBy filter. And the orderBy filter allows us to do the actual sorting. So, as our first parameter to orderBy we'll pass sort.field, which is the field that we need to order by, and then we'll also pass sort.order, which will be false to sort descending, and true to sort ascending. So that is our table row, but now in here we will have our table data elements, right. And for table data elements we need to use the same ng-repeat that we used above. So we'll say, ng-repeat for each field in fields. Because remember whatever fields we displayed as the headers, we also want to display in the records. So in here, we will go ahead and display contact[field]. But remember that dot is an array and so we only want to display the zero with element of that array. Okay so I'm gonna save that, and we'll come back to the browser now. And if we refresh the page, and there you go. You can see the data that we added to our database. So we're sorting by first name. I can go ahead and reverse sort. I can sort by the last name instead. If I go ahead and query, let's see I can look for Jack. I can look for last name Quinn and I get Quinn. I can just look for the letter qe and I get Queen and Quinn, excellent. So we have querying and sorting working just fine. However, that's not quite all that we wanna do here, because you might recall from the demo that we wanna be able to click these different rows and go to individual pages. Now, unfortunately there's no easy way to make an entire table row an anchor element. So, what we need to do instead is listen for a click event on the table row with ng-click, and when a click happens we want it to call a function. So let's go ahead and write that function first. So what we want to do is add a function to scope that we'll call show, and this function will take the ID of an individual record, and we want to change the URL by going to the /contact/ whatever that ID is. Now the way to do this is with the location provider, so we have to request this in our function, $location and then down here we can say $location.url equals contact/id. So, now, back over here, we need to add this to our ng-click directives, so we'll say show, and we'll pass it, just like that. So, now, if we come back and refresh this page, we should be able to click Ellery Queen here. And when I click it, notice that our URL changes to contact/two. Of course our template disappears because we're no longer on the /contacts page but we don't have a route for this page so nothing is displayed yet. If I go back, I can click on somebody else and the same thing happens. There's one more thing I wanna do, and that is, notice the field names here, firstName and lastName. We're doing this in CamelCase which is how these will be stored in the database. However, that's not quite as user friendly as it could be to look at and read. So, let's write an Angular filter that will change this to a more easy-on-the-eyes format. We'll create a new file for this, public/source/filters.js. And as we've done in previous files, we'll go ahead and request the Angular module here and then let's create a filter and we will call this, LabelCase. And so what we have to do here is return a function. This function will take some input which will be the name of a field and it will return the LabelCase strength. So let's set input equal to input.replace, and we'll use a regular expression, where we search for any uppercase letter from a to z. And we'll do a global search. And let me wrap that in parentheses cuz we wanna capture that uppercase letter. And we're gonna replace that with space $1. Now if you're not familiar with JavaScript replace function, we pass a regular expression as the first parameter here, whatever text matches the regular expression will be replaced by our second parameter or string. Now I'm capturing that capital letter by putting parentheses around this a to zed portion, and that captured value will be available as $1. So basically, if we have first name, the portion that gets captured by the regular expression is the capital letter N, and that would get replaced by space $1, and $1 is the N, so this will be replaced with first space Name. And this, of course, we'll work with much longer, so if we have the very first name. This will in turn become the very first name with spaces between each one of the words. Now after we do that the only thing left is to capitalize the very first letter so we can return which is the first letter of the string. And we'll return that to upper case, and then we can just do plus input.slice from one to the end of the string. So this will just capitalize the first letter and then concatenate that to everything except the first letter of the string. So that should work just fine. Let's not forget to add this file again. So we'll go to public main, and underneath factories here, we will add src/filters.js. And then, here in our list.html, where we have field here, we'll go ahead and pipe this through LabelCase, and that should be all that's required to properly format our table heading text. So, if I refresh this, you can see that now we have first space name and last space name with each word being capitalized. Excellent. So, now we are successfully displaying our list of contact records to the user. So, now that we have a way to display our list of records, we're ready to build a way for a user to create records from within our applications interface. And that's what we'll look at in the next couple of screen casts.

Back to the top