Skip to content

Instantly share code, notes, and snippets.

@nimitmaru
Created May 15, 2015 15:47
Show Gist options
  • Save nimitmaru/43b2827a5bb025590256 to your computer and use it in GitHub Desktop.
Save nimitmaru/43b2827a5bb025590256 to your computer and use it in GitHub Desktop.

#Angular part 2

Part 1 Recap

In the last workshop we covered the basics of building an Angular app. As a recap the steps included:

  1. Start with a default file to serve, typically index.html
  2. Include the Angular Library source file like any other javascript file
  3. Create a main file, app.js, that defines and initiates the angular app 'Education'
  4. Inside app.js should be a controller named 'SchoolController', that defines a school property on the $scope object, containing details about Fullstack Academy
  5. On index.html, set ng-app to encompass the html body
  6. In another div, declare the controller responsible for the scope of class schools

Objectives

We were able to display a single object with multiple properties on the last workshop. The objective today is to display an array of multiple objects with similar properties.

Setup

To start, we need to fork the repository.

  1. Fork the repository to your account.
  2. Go to your own repositories on GitHub and clone the fellowshipt repository link.
  3. Use the link in the git clone then follow the rest of the commands.
$ git clone https://github.com/alwang85/fellowship.git

Display an array of 3 schools

Leveraging the existing template, try displaying the contents of the following array:

    schools = [{
          students: 28,
          name: "Fullstack Academy",
          history: "2 years",
          fellows: ["Seema", "Ivan", "Linda", "Sarah"],
          motto: "code for life"
        },{
          students: 50,
          name: "General Assembly",
          history: "2 years",
          fellows: ["Sam", "John", "George"],
          motto: "all you can join"
        },{
          students: 17,
          name: "Joescript Academy",
          history: "Joe years",
          fellows: ["Joe", "Joe", "Joe", "Joe"],
          motto: "got Joe?"
     }];

Bonus: When you are done, try out what happens when you delete a school from the schools array without modifying index.html!

Perhaps a DRYer way: ng-repeat

Chances are on index.html you did something such as {{schools[0].name}} for all 3 schools. That certainly goes against the coding mantra of DRY (Don't Repeat Yourself)!

Fortunately, Angular has a built in directive called ng-repeat. Check out the following resources and then refactor your code using ng-repeat!

https://docs.angularjs.org/api/ng/directive/ngRepeat

http://randomjavascript.blogspot.com/2014/09/building-angular-list-using-ng-repeat.html

Once you got ng-repeat to work, try copying the same objects inside the schools array and add a duplicate copy into the array!

Hints / Syntax

ng-repeat takes the form of <div ng-repeat="(key, value) in myObj"> ... </div> for Objects, and <div ng-repeat="element in elements"> ... </div> for an Array.

{{element.name}} gives you access to the name property of this element in an Array. You may have noticed that every repetition has different values, even though they are referenced with the same "element.name". Every element in ng-repeat has its own scope!

Also, Angular by default does not allow duplicate elements to be shown in ng-repeat, since it would be much harder to manipulate the DOM with multiple objects having the same data;

Recap

When we displayed the array of 3 schools by manually adding <div>'s, it was very tedious to manage all of the elements of the array. Worse, when the manually added elements had a different length of what's available in the array, the code broke.

In many applications, you may not be able to know beforehand the elements of what you wish to display. ng-repeat is one of the BEST directives in Angular that helps front-end development become more DRY.

ng-repeat keeps track of the different elements created, and also maintains a separate scope for each element. This allows the same function on the scope to be passed the different values of each event! Find out more about the benefits of separate scope in the last Bonus step!

Next Steps / Bonus

  • Instead of displaying the fellows as an array, try nesting ng-repeat!

  • You can add a class to an element by adding ng-class="classname" to an element

    • Check the docs for ng-class-odd and ng-class-even to see how they work together with ng-repeat!
  • Check the documentation for ng-click , define a function in the controller, and experiment with ng-click! A good sample function would be:

      $scope.tellMe = function(element){
          console.log(element)
      };
    

#Pokedéx Upgrade

Introduction

Ash Ketchum, after a long and fulfilling career as a Pokémon trainer, has decided to make the switch to web development. Oak Industries, the manufacturer of the Pokédex, has employed Ash, and has assigned him his first project: upgrade the Pokedéx to a web-based single page application. You will assume the role of Ash, and begin the process of making a new Pokedéx. Today we will be working on one feature: listing all of the Pokemon on the Pokedex onto a single page!

Getting Started

  1. Fetch and clone the workshop from the GitHub Repo
  2. Run npm install after retrieving the repository. This repository will include the index.html, app.js, and style.css files.
  3. Run npm start after installing the files, and we're ready to start!

Puting it into HTML

Inside of our index.html file, let's place our Pokedéx onto the page statically.

Here is the current HTML snippet for the Pokedex:

<div class="pokemonster"> 
    <h1>Name</h1>
    Name of Pokemon 
    <h3>Type</h3>
    Type: Grass, Water, Fire
    <img src='pokemon.com'></img>
</div> 

Place the snippet inside the <div> with the ng-controller="PokedexController" attribute that was included in the index.html file.

Professor Oak's Conundrum

Professor Oak, CEO of Oak Industries, is using a highly manual and inefficient process to update all of the Pokemon in the Pokedex. He is replacing the fields Name of Pokemon, Type: Grass, Water, Fire, and img src of each Pokemon's attributes one by one! Can you think of a better way to update the Pokemon in a way that not only saves time but also allows for greater control of your application? If you do not, have no fear, because I would like to introduce you to an Angular directive called ng-repeat!

This directive let's you iterate over a collection (ex. array or object) and repeat a snippet of code for each of the items in the collection. Angular creates a new $scope instance for each repeated snippet of HTML. This allows you to input properties of all of the Pokemon in the $scope.pokeList array in app.js by including ng-repeat in your .pokemonster div!

Gotta Catch Em' All!

Inside our app.js file, let's take a look at what's inside of our PokedexController :

app.controller('PokedexController', function($scope){
	$scope.pokeDex = [
		{
		name: 'Charmander',
		type: 'Fire',
		img: 'http://img.pokemondb.net/artwork/charmander.jpg'
		},
		{
		name: 'Squirtle',
		type: 'Water',
		img: 'http://img.pokemondb.net/artwork/squirtle.jpg'
		},
		{
		name: 'Bulbasaur',
		type: 'Grass',
		img: 'http://img.pokemondb.net/artwork/squirtle.jpg'
		},
		{
		name: 'Pikachu',
		type: 'Electric',
		img: 'http://img.pokemondb.net/artwork/pikachu.jpg'
		}
	];
});

As you can see, there are four Pokemonster objects in our $scope.pokeDex array that contain the properties we would like to include in our Pokedex. Let's take a look at how we can modify our .pokemonster div to populate all of our Pokemon and their properties into the Pokedex page.

  • Let's include ng-repeat into our div

      <div class="pokemonster" ng-repeat='pokemonster in pokeDex'> 
          <h1>Name</h1>
          Name of Pokemon 
          <h3>Type</h3>
          Type: Grass, Water, Fire
          <img src='pokemon.com'></img>
      </div>
    

The ng-repeat will iterate through the array of pokemon objects located on the $scope.pokeDex array in our PokedexController and create a new .pokemonster div for the length of the array. Every instance of each pokemon object will be referred to in each div as pokemonster.

  • Now, let's format our template to display the properties of each pokemon

      <div class="pokemonster" ng-repeat='pokemonster in pokeDex'> 
          <h1>Name</h1>
          {{pokemonster.name}}
          <h3>Type</h3>
          {{pokemonster.type}}
          <img ng-src="{{pokemonster.img}}"></img>
      </div> 
    

Since pokemonster in each of our divs' is a reference to each pokemon object, we have access to each pokemon's properties! Our Pokedex page should now be populated with the name, type, and image of each Pokemon! Take a look at the page in your Chrome browser, and use the Chrome element inspector to see how the properties for each pokemon have been inputted in each div!

Using Angular's ng-repeat for DRY HTML

Review: interpolating values with {{ }}

You already know how to interpolate values in Angular using {{ }}.

For example, imagine you have the following Angular controller that contains $scope.flavor:

app.controller('home', function($scope) {
	$scope.flavor = "chocolate";
});

In your HTML, you could access $scope.flavor easily by doing this:

<div ng-controller="home">
	<div>{{ flavor }}</div>
</div>

This will render the word "chocolate" in place of {{ flavor }}.

A problem: interpolation for arrays

Now let's alter the controller slightly by giving it an array of flavors:

app.controller('home', function($scope) {
	$scope.flavors = ["chocolate", "vanilla", "strawberry"];
});

How might you go about getting all three of these flavors into your HTML? Discuss this with your partner before continuing.

Some unsavory solutions

You could simply interpolate {{ flavors }} :

<div ng-controller="home">
	<div>{{ flavors }}</div>
</div>

But this is going to show the array in it's array syntax directly to your users. Not so pretty.

You could also do this:

<div ng-controller="home">
	<div>{{ flavors[0] }}</div>
	<div>{{ flavors[1] }}</div>
	<div>{{ flavors[2] }}</div>
</div>

That works, but as programmers such repetition should be a red flag to us indicating that we should find a more eloquent solution.

Imagine:

  • What if we had 31 flavors?
  • And what if the next day we removed 5 of those flavors?

We would need to edit our HTML every time our data changes.

Using ng-repeat

Thankfully Angular provides a solution for us that keeps our HTML DRY and dynamically interpolates values into our markup so that we don't have to go and make edits every time something changes in our JavaScript.

Welcome to ng-repeat, a built-in Angular directive that will repeat a given template for all items in a collection of data.

Using our same controller from above with $scope.flavors, here is how we can use ng-repeat in our HTML:

<div ng-controller="home">
	<div ng-repeat="flavor in flavors">{{ flavor }}</div>
</div>

The middle line there is where all of the magic happens. Inside of the ng-repeat, it is flavors that is referencing the data collection on our scope ($scope.flavors).

Then flavor refers to each individual item in the data collection. There's nothing special about the word flavor itself; we could have used any word here:

<div ng-repeat="book in flavors">{{ book }}</div>

This will still get us the same results, it's just less intelligible to humans reading the code.

Try it out

Using ng-repeat with objects

With your partner, try writing out your HTML for the following controller:

app.controller('home', function($scope) {
	$scope.nycData = {
		name: "New York City",
		population: "8.4 million",
		founded: "1624"
	};
});

Your markup should display the data like this (don't worry about the order for now):

founded: 1624
name: New York City
population: 8.4 million

Using ng-repeat with an array of objects

With your partner, try writing out your HTML for the following controller:

app.controller('home', function($scope) {
	$scope.cityData = [
		{
			name: "New York City",
			population: "8.4 million",
			founded: "1624"
		},
		{
			name: "Los Angeles",
			population: "3.8 million",
			founded: "1781"
		}
	];
});

Your markup should display the data like this (don't worry about the order for now):

founded: 1624
name: New York City
population: 8.4 million
founded: 1781
name: Los Angeles
population: 3.8 million

Solutions

Below are possible solutions to the coding challenges.

Using ng-repeat with objects

<div ng-controller="home">
	<div ng-repeat="(key, value) in nycData">{{ key }}: {{ value }}</div>
</div>

Using ng-repeat with an array of objects

<div ng-controller="home">
	<div ng-repeat="city in cityData">
		<div ng-repeat="(key, value) in city">
			{{ key }}: {{ value }}
		</div>
	</div>
</div>

Resources

Angular Repeat Workshop

Welcome to Day 2 of Angular! Not so bad, right?

By this point, you have probably begun to understand how Angular's $scope (just an object that contains values and methods) interacts with the HTML template. As a refresher, check out the code below.

angular.module('testApp')
	.controller('TestCtrl', function ($scope){
		$scope.foo = 'bar';
	});

This $scope variable can be easily accessed in your HTML template view like so...

<p> {{ foo }} </p>

..and would render to:

bar

Now imagine that you had an array of names in your controller that you wanted to display in a simple ordered list. Go ahead and write some HTML that would do this.

angular.module('testApp')
	.controller('TestCtrl', function ($scope){
		$scope.names = ['John','Cat','Arya'];
});

Wouldn't it be so tiresome, not to mention repetitive, to display these one at a time?

<ol>
	<li> {{ names[0] }} </li>
	<li> {{ names[1] }} </li>
	<li> {{ names[2] }} </li>
</ol>

Which would look like this rendered:

1. John
2. Cat
3. Arya

Of course, if you were to add a new element to the array, you would have to manually go back and add another list element to the HTML file. Also, if you wanted to make any changes -- say bolding the names in the list -- you would have to change each item individually. Gee, wouldn't it be nice if there were a way to loop through the array and display the contents? Behold, Angular does have a perfect tool just for dealing with arrays!

<ol>
	<li ng-repeat="name in names">{{name}}</li>
</ol>

In the line above, 'names' refers to the array $scope.names, while 'name' is a user-declared variable that represents each element in the element. It could be any word you'd like, but you will use it to display the array item, so it is best to make it sensible.

This would render out the same as the much more repetitive code before, but now, if you were to add another name to the array (or remove one), the ngRepeat would automatically render this when the webpage loaded.

angular.module('testApp')
	.controller('TestCtrl', function ($scope){
		$scope.names = ['John','Cat','Arya', 'Eddard', 'Robb']; //pushed more names
});

Which would look like this rendered:

1. John
2. Cat
3. Arya
4. Eddard
5. Robb

Now you try! Write some HTML that will print out the cheesy poem below.

angular.module('testApp')
	.controller('TestCtrl', function ($scope){
		$scope.poem = ['Roses are red','Violets are blue','I learned Angular', 'And you can too!'];
});

####Solution I know, I know. You're still cringing from the poem.

<div>
	<p ng-repeat="line in poem">{{ line }}</p>
</div>

Let's make it a little more complicated. Now imagine that you had an array of objects.

angular.module('testApp')
	.controller('TestCtrl', function ($scope){
		$scope.piedPiper = [
			{name: 'Richard', title: 'CEO'},
			{name: 'Erlich', title: 'Board Member'},
			{name: 'Big Head', title: 'Dreamer'}
		];
});

Try to figure out how you would use ngRepeat to display the names and titles of each tech visionary in a list like before.

####Solution

  1. {{visionary.name}}, {{visionary.title}}

Which would look like this rendered:

1. Richard, CEO
2. Erlich, Board Member
3. Big Head, Dreamer

Pretty cool, right? Now you're seeing how ngRepeat can be used to keep your code DRY and easy to modify. Let's see what else we can do!

Say you wanted to make a function that takes the list above and pops up an alert when you click on them. For example, if you click on Richard, an alert would display "Richard is the best!" Here's the function.

angular.module('testApp')
	.controller('TestCtrl', function ($scope){
		$scope.piedPiper = [
			{name: 'Richard', title: 'CEO'},
			{name: 'Erlich', title: 'Board Member'},
			{name: 'Big Head', title: 'Dreamer'}
		];

		$scope.favorite = function (name) {
			alert(name + " is the best!")
		}
});

Take a stab at solving this on your own. Where would you attach the function? From where would you get the 'name' argument that it takes?

####Solution

<ol>
	<li ng-repeat="visionary in piedPiper" ng-click="favorite(name)">
	{{visionary.name}}, {{visionary.title}}</li>
</ol>

Here we're using ngClick to attach the 'favorite()' function to each list element as we ngRepeat over them. As we do each one, we pre-fill the argument in each line with the name of the person on this list. Now, when the function is called, the right name will be called in the function.

###Bonus

Finally, let's check out another one of Angular's super slick features... filters! Filters can make your ngRepeat functions even more powerful. Let say you wanted to display a list of items you were selling. First, go ahead and display the items in my junkPile in an order list as we've done before.

angular.module('testApp')
	.controller('TestCtrl', function ($scope){
		$scope.junkPile = [
			{item: 'CRT TV', cost: 10.5},
			{item: 'Barely used Segway', cost: 20},
			{item: 'Old Android phone', cost: 2}
		];
});

####Solution Part 1

<ol>
	<li ng-repeat="junk in junkPile">
	{{junk.item}}: {{junk.cost}}</li>
</ol>

Great. Though it does appear to be lacking... Where're the '$' signs? What if you wanted to sort it by price?

1. CRT TV: 10.5
2. Barely used Segway: 20
3. Old Android phone: 2

You could certainly work with data and formatting until it looked more presentable, but would you believe that there's an easier way? Angular filters can be combined with ngRepeat functions to automatically sort or add formatting. For example, the reverse filter, well... reverses a string. It is applied by using the pipe character that is usually above the return key ('|') like so:

$scope.word = 'racecar' --> <p>{{ word | reverse }}</p> --> Oops! Bad example.
$scope.word = 'palindrome' --> <p>{{ word | reverse }}</p> --> 'emordnilap'

Now, try to use filters to display our list of junk from earlier, but now sort it by price and format the price properly.

####Solution Part 2

<ol>
	<li ng-repeat="junk in junkPile | orderBy:'-cost'">
	{{junk.item}}: {{junk.cost | currency}}</li>
</ol>

Very good! Hopefully, you can see how Angular provides programmers with many tools for making our lives a little easier. You're now well on your way to becoming an Angular master!

AngularJS – Intro to ng-repeat

Objective:

Participants will continue to explore AngularJS directives, particular the flexibility and power of ng-repeat.Ã�Â

Tasks:

  1. 1.Participants will create a create a simple AngularJS application. This will serve as a review to the concepts already introduced. ( Note: was not able to get to this part of the workshop Ã� due to time )Ã�Â
  2. 2.Participants will attach a controller to the AngularJS application to add logic to an HTML document/view. This will server as a review to the concepts already introduced. (Note: was note able to get to this part of the workshop due to time)Ã�Â
  3. 3.Participants will be able to obtained data from an API and populate an AngularJS front-end using the data.Ã�Â
  4. 4.Participants will use ng-repeat to create a view that displays data found in the public GitHub API. ( Note: with more time, I would increase difficulty of activity) Ã�Â

1. Introduction

Today we are going to continue learning about AngularJS directives, particularly the ng-repeat directive.

Review: What is an AngularJS directive?

Solution : Directives are markers on a DOM element (such as an attribute, element name, or CSS component) that attach a special behavior to it. Directives are powerful tools to manipulate the DOM. You can even create custom directives, which we will be doing so in the next workshop.

If you need a refresher on directives, please take a few minutes to look through the AngularJS documentation on directives ( https://docs.angularjs.org/guide/directive) to learn more about directives.

Let's take a look at a JSON object that we might receive when making call to an API. Let's use the GitHub API. I am going to use public data that's made available by GitHub through their API.

2. What is ng-repeat?

A crucial aspect of web development is displaying data. Often, we have to work with repeating data. This is where the Angular directive, ng-repeat comes in handy!

In this workshop, we will be focusing on ng-repeat, which helps us loop through data contained in array.

Example:

Let's look at a quick example

Take a few minutes an look over the Angular documentation on ng-repeat to learn more: https://docs.angularjs.org/api/ng/directive/ngRepeat

Activity

We are going to be using GitHub's Public API to display information in our view. The information that we get back is inside an array, so ng-repeat will be incredibly useful to display this information.

Let's look at the public repo information that is made available for my repos using the GitHub API: https://api.github.com/users/carlos-r-mendoza/repos. You can do the same for yours! Just replace my GitHub username with yours.

This is what part of what we get back looks like:

As you can see, this is an array of objects, with each object representing a repo. Check it out for yourself by clicking on the link above!

Our goal in this workshop is to create a small Angular app that will display some of this information using ng-repeat.

Step 1:** Fork Over The Project At… (Note: to be developed), npm install, and enter node.js in your terminal.**

Step 2: Obtaining GitHub Data

We have developed a simple Angular app for you (Note: ideally students would review this process while doing workshop).

We are using a library that makes the call to the GitHub API to retrieve repo information. Got to routes/index.js and replace the username inside our route with your GitHub username (or mine!).

When we hit the route, your data will be available to us in the front-end to be displayed!

Step 3: Go to the app's controller and noticed that we are invoking the getRepoInfo() function and applying it to $scope.myRepoInfo. This makes our data now available in the front-end.

[image to be included]

Step 4: Now go to our HTML file. Using what you've learned about ng-repeat, populate the view with the name of all your repos, the link on GitHub to each of your repos, and the description of each repo.

Bonus: Try displaying your GitHub profile image and username somewhere on the page!

ng-repeat

For all your repeating/looping needs (in Angular)

Intro and Basic Examples

At the most basic level, ng-repeat is an angular directive that allows you to loop over a collection What does this look like?

Below is a basic example of ng-repeat:

    //given $scope.numberArr = [1,2,3,4]

    <div ng-repeat=“number in numberArr”>
        <span>{{number}}<span>
    </div>

Note the $scope. numberArr must be on the scope of the controller that the ng-repeat is in otherwise it will not print. More on scope later.

ng-repeat in the above example will loop through numberArr and the current element being processed in the array will be the loop variable 'number'.

It is similar to a forEach loop:

    numberArr.forEach(function(number){
        console.log(number);
    })

Each element in numberArr will be named number in this callback function.

In the ng-repeat example (and forEach example) number could be named anything. It could be named turtles and it would still work:

    <div ng-repeat=“turtles in numberArr”>
        <span>{{turtles}}<span>
    </div>

But it is good practice to associate the name of the loop variable and collection for readability.

In the ng-repeat example however, each number would be interpolated and put in its own span. The HTML produced by the ng-repeat example above would look something similar to below:

    <div ng-repeat=“number in numberArr”>
        <span>1<span>
    </div>

    <div ng-repeat=“number in numberArr”>
        <span>2<span>
    </div>

    <div ng-repeat=“number in numberArr”>
        <span>3<span>
    </div>

    <div ng-repeat=“number in numberArr”>
        <span>4<span>
    </div>

More Complex uses

Question: Given an array of users (which are individual objects) we want to show the user's name, email, and last time they logged in. Try it out!

    var $scope.usersArr = 
    [{name: "Christian", email:"christian@fsa.com", lastLoggedIn: '2015-05-11'},
    {name: "Sam", email: "sam@fsa.com", lastLoggedIn: '2015-05-08'}];

    <div ng-repeat="user in users">
        //Your code here
    </div>

Solution:

    <div ng-repeat="user in users">
        <span>{{user.name}}</span>
        <span>{{user.email}}</span>
        <span>{{user.lastLoggedIn}}</span>
    </div>

Given the above example, the user's name, email, and lastLoggedIn keys exist on the user object so we can access their values. They would be interpolated and put on the DOM in their own spans.

###What about scope?

Knowing what scope you are currently in is super critical when working with Angular in general, some of the biggest headaches you will get will come from scopes you unknowingly nested by accident or trying to access some data that is outside of the scope of some controller.

It is important to note that when using ng-repeat that every element in that given array will create it's own scope. Keep that in mind!

    <div ng-repeat="user in users">
        <span>Christian</span>              // This is 
        <span>christian@fsa.com</span>      // a separate    
        <span>2015-05-11</span>             // newly created scope different from below
    <div>

    <div ng-repeat="user in users">
        <span>Sam</span>                    // This is
        <span>sam@fsa.com</span>            // a separate		
        <span>2015-05-08</span>             // newly created scope different from above
    </div>

###Is ng-repeat just for arrays?

Great question! you can use ng-repeat on objects as well but the syntax will be a little different:

    <div ng-repeat="(key, value) in data">
        <span>{{key}} : {{value}}<span>
    </div>

The above code would print out all of the key value pairs in data. Pretty Cool!

###What else can ng-repeat do? Unfortunately when you use ng-repeat duplicate values in an array will cause a problem. But there is a solution, tracking!

There are many ways to track items in a collection instead of using the default tracking, the most basic is by index.

You use the track by expression in your ng-repeat to do so. Below is a basic example:

<div ng-repeat="grade in [42, 75, 90, 75] track by $index">
  {{n}}
</div>

Refer to the [ng-repeat docs] for more complex ways of tracking. [ng-repeat docs]: (https://docs.angularjs.org/api/ng/directive/ngRepeat)

###Nesting ng-repeats! Consider the following hypothetical student student:

var $scope.student = 
{ name: Christian,
  age: 23,
  grades: [85, 83, 76, 50, 76, 90, 80],
  daysAbsent:['2015-05-01', '2015-03-25', '2015-02-28'],
  awards: ['The coolest', 'Most helpful', 'Greatest backpack']
}

What if we wanted to show all of this student's information? In the student object, there are 3 fields that contain arrays.

Lets use ng-repeat!! Give it a go!

Solution:

    <span>Grades:</span>
    <div ng-repeat="grade in student.grades">
        <span>{{grade}}</span>
    </div>
    <div ng-repeat="day in student.daysAbsent track by $index">
        <span>{{day}}</span>
    </div>
    <div ng-repeat="award in student.awards">
        <span>{{award}}</span>
    </div>

###Extra Credit What if you were given $scope.students which was an array of students?

Movie Night! An Exercise In Angular ngRepeat

Introduction

The local 5-seat cinema puts on classics twice a month, but only releases their list of shows the day before. You will be building the page that displays this list. It should display all the movies that the cinema will be playing as well as the available show times for each movie. Users should be able to check seat availability for each showtime.


Getting Started

  1. Fetch the Workshop

  2. Include Angular

  3. Relevant Files

    Please write your code to "ngRepeat.js" and "ngRepeat.html" only.

The Controller

  1. Adding the movie data

    Take a look at "ngRepeat.js". You'll find our controller, "movieCtrl", which contains an array called "$scope.movies", an object called "responses" and a function called "$scope.response". Ignore the last two for now. "$scope.movies" shall serve as our movie list. The movie list will need some data; add the data found in "movie_data.js" to $scope.movies.

  2. Add the controller to the body tag

    Our Angular application "movieApp" has already been registered to "ngRepeat.html", do the same for the "movieCtrl" where indicated in the HTML.

ngRepeat

  1. Adding the list of movies

    Now that our controller has been registered and we have our data, we can start adding the list of movies to the page. How might we do this?

    Instead of creating tags manually for each movie on our list, we can use the ngRepeat directive. ngRepeat is assigned as an attribute and takes an expression that iterates through a given collection and creates a new container per item (e.g. ngRepeat="item in collection") as well as a new scope with each new container.

    Think about what the list of movies should display at this point (HINT: the title). How might we display this for each movie in our list? (HINT: ngRepeat="item in collection"; "{{item.property}}")

  2. Adding the list of show times

Now we add showtimes for each movie. ngRepeat should again receive an expression that iterates through a collection, but what is the collection in this instance?

Could not complete this section of the workshop

Checking for seats

  1. Click events part 1

Users should be able to click on a particular showtime and get a message telling them whether seats are still available. This can be done by adding "ng-click=somefunction()" as an attribute to the tags housing showtimes.

What function should ngClick invoke? Let's take a look at our controller. $scope.reponse doesn't seem to be doing much, and takes a parameter called "seats". Looks promising.

There is also that object in our controller called "responses". See if you can implement "$scope.response" to give the correct response depending on the number of seats available.

#Introduction
You've already learned how Angular makes it easy to display Javascript values on the scope to the user. The scope, you'll remember, is the context within which expressions in the HTML, like {{variableName}}, are executed. For instance, in the example below, name and quotation are both variables in the scope defined by the PersonController controller. You can access them within the div that has the PersonController controller attached to it, but not elsewhere.
> var app = angular.module('app',[]);
> app.controller('PersonController', function($scope) {
> $scope.name = "Inigo Montoya";
> $scope.quotation = "You killed my father. Prepare to die.";
> });
><body ng-app="app">
> <div ng-controller="PersonController">
> <h2>{{name}}</h2>
> <p>{{quotation}}</p>
> </div>
></body>
Such a controller allows us to display information to the user--and also allows us to change it without the lengthy method chains and tangle of callbacks we might find ourselves entangled by when using jQuery. If "name" or "quotation" were to change in response to an ng-click event, Angular would detect this change and alter the information in the DOM without requiring us to update it explicitly.
What if we want to display information from an array, though? Suppose that the controller above looked like this:
> app.controller('PersonController', function($scope) {
> $scope.people = [
> { name: "Inigo Montoya", quotation: "You killed my father. Prepare to die." },
> { name: "Vizzini", quotation: "Inconceivable!"},
> { name: "Dread Pirate Roberts", quotation: "Bitcoin is the currency of the future." }
> ];
> });
One might, of course, begin to write code like the following:
> <div>
> <h2>{{people[0].name}}</h2>
> <p>{{people[0].quotation}}</p>
> </div>
> <div>
> <h2>{{people[1].name}}</h2>
> <p>{{people[1].quotation}}</p>
> </div>
And so on and so forth. But this involves repeating ourselves a great deal, which is always a bad idea.
The code above would be a pain to edit and to maintain over time. The length of the array "people" would be tied to the amount of markup that we would need to write, which would be very awkward--iit is generally just ugly code.
#Introducing ng-repeat
These problems can be solved with ng-repeat.
Ng-repeat allows you to cycle through an array (or object--but for now I'll only talk about arrays) and use the contents of each element in the array in a newly created scope. Ng-repeat is itself an attribute directive, which is placed into the html element (or angular element) which will be repeated, along with its children, once for each element in the array.
This will make more sense after an example. For instance, the prior markup could be replaced by the following:
> <div ng-repeat="person in people">
> <h2>{{person.name}}</h2>
> <p>{{person.quotation}}</p>
> </div>
This will cycle repeatedly through the "people" array in the scope. For each element, angular will create a new div, along with child <h2> and <p> elements. The loop variable "person" will have sequential elements from the array "people" assigned to it, in a new scope created for each individual repetition. So each repetition of the <div> will contain new information from sequential elements of the array.
Of course, it is possible to use the contents of the scope created by ng-repeat in arbitrary expressions, just like you can use the contents of any variable in the scope in expressions. The following code would tell you what the numbers from 1 to 8 make when multiplied by 13.
> <div ng-repeat="n in [1,2,3,4,5,6,7,8]">
> <p>{{n}} multiplied by 13 makes {{n * 13}}</p>
> </div>
And the following code would tell you which of the numbers from 1 to 10 are even or odd.
> <div ng-repeat="n in [1,2,3,4,5,6,7,8,9,10]">
> <p ng-show="n % 2 == 0">{{n}} is even.</p>
> <p ng-show="n % 2 == 1">{{n}} is odd.</p>
> </div>
Finally, Angular is usually smart enough to add to or take from the DOM when extra elements are added or taken away from the array, just like Angular is with non-array variables.
##Exercise
Write a program which uses the following property on the scope.
> $scope.people = [
> { name: "Luke Skywalker"},
> { name: "Darth Vader", evil: true},
> { name: "Jar-Jar Binks", irritating: true},
> { name: "Yoda"},
> { name: "Mara Jade"},
> { name: "Leia Skywalker"},
> { name: "Grand Moff Tarkin", evil: true},
> { name: "Wicket", irritating: true}
> ];
Use ng-repeat to display the names in question. Alter the manner in which the names are displayed, if the person is evil or irritating.
Hint: You'll probably want to review ng-class, if you don't recall how to use it.
#More on the scope defined by ng-repeat.
As you've seen, ng-repeat creates a new scope for each repetition, setting the loop variable to have the value of one element in the collection through which it is iterating. There are other special properties that Angular automatically creates in the scope of ng-repeat.
$index is one of these. This is the index of the element through which ng-repeat is cycling. The following code, for instance, would number the characters through which it cycled.
> <div ng-repeat="person in people">
> <h2>{{$index}}:{{person.name}}</h2>
> <p>{{person.quotation}}</p>
> </div>
There are a few other special properties that ng-repeat automatically creates. $first is true only only in the scope tied to the first element in the array; $middle is true on everything but the scopes tied to the first and last elements; $last is true only on the last element. $even and $odd are similarly true in fairly predictable fashions.
##Exercise
Alter the program that you wrote above, so that it uses at least two of the variables mentioned above. For instance, you could add alternating colors to the background, add special formatting for the first and last elements, or number them.
#A potential bug with ng-repeat
Suppose you measured the number of people who came to Full Stack Academy per day, and stored the results in an array. The results might look something like this:
> $scope.peopleAtFSA = [58, 60, 58, 59, 61, 32, 0, 60, 58, 65, 59, 60, 22, 0, 65, 55, 59, 59, 60];
You might than try to display this as following.
> <div ng-repeat="n in peopleAtFSA">
> {{n}}
> </div>
This would cause an error, however, because many of the numbers in the array repeat. Angular does not normally allow repeated elements within ng-repeat. This is because Angular likes to maintain a one-to-one mapping between array items and DOM elements, and repeated elements make it so that the default way of mapping array items to DOM elements does not work.
It is, however, possible to give Angular other ways of keeping track of elements within the collection. You can tell Angular to track these elements by $index, for instance:
> <div ng-repeat="n in peopleAtFSA track by $index">
> {{n}}
> </div>
#More Exercises
##Nested ng-repeat.
Suppose that the data used in one of the prior programs had arrays within the objects in the array in question. That is, suppose it looked like this:
> people = [
> { name: "Luke Skywalker", friends: ["Leia Solo nee Skywalker", "Darth Vader", "Mara Jade"]},
> { name: "Darth Vader", evil: true, friends: ["Grand Moff Tarkin"] },
> { name: "Jar-Jar Binks", irritating: true, friends: []},
> { name: "Yoda", friends: ["Luke Skywalker"]},
> { name: "Mara Jade", friends: ["Luke Skywalker", "Leia Solo"]},
> { name: "Leia Solo nee Skywalker", friends: ["Luke Skywalker", "Han Solo"]},
> { name: "Grand Moff Tarkin", evil: true, friends: ["Darth Vader"]},
> { name: "Wicket", irritating: true, friends: ["Luke Skywalker"]}
> ];
Alter your program to use nested ng-repeats to display this information. You already know everything that you need to make the ng-repeats. The repeats will look something like this--with, of course, different names for the properties on the scope that you are iterating over.
> <div ng-repeat="higherElement in higherArray">
> ..
> <div ng-repeat="lowerElement in higherElement.lowerArray">
> </div>
> ..
> </div>
##Using ng-click with ng-repeat.
As mentioned above, when you add an element to an array used by ng-repeat, Angular will generally pick up on the change and alter the user-visible DOM. Write a program which displays 10 random numbers from 1 to 100 using ng-repeat. Add an ng-click element which will add between 1 and 4 randomly chosen elements to the array when it is clicked. I.e., clicking the elemnt might add [32] to the array; it might add [23,40,90]; or it might add [56,53] to the array. Watch out for duplicate elements!

Favorite Foods

Introduction

Objective

Many times when you are building a dynamic site, you will need to iterate over an array or object and display each element on the page. For example, in the TripPlanner workshop, the dropdown menus for the hotel, restaurant, and activity options can (and should) all be placed on the DOM by iterating over an array.

With jQuery, this process is relatively straightforward, but not exactly the best solution for large-scale apps. By the end of this workshop, you will see that Angular's approach to this problem is more robust and results in cleaner code.

The objective of this workshop is for you to use Angular's ngRepeat directive to list out the names and favorite foods of five members of your cohort.

Generating Data

Before we get started, ask your neighbors (or just Slack each other) what their three favorite foods are. You should gather information from five people, for a total of fifteen favorite foods. For now, just keep this information to the side; we'll use it later in the workshop.

Creating Your App

Server Setup

A project has already been scaffolded out for you to get you started. You can clone the repo using: git clone https://github.com/FullstackAcademy/favorite-foods.git

The repo includes a simple Node/Express server (don't forget to NPM install!) with a single GET route to serve up your index.html. However, the frontend has not been built. That's your next task!

Frontend Setup

By now, you may or may not feel comfortable creating an Angular app from scratch (instantiating your app, creating a controller, etc.). Use this workshop as an opportunity to go through the process as a pair. If you need some help, use the hints below.

Hint: Accessing Angular

Use bower to install Angular. Then make sure you give your app access to the Angular library in your index.html using a <script> tag.

Hint: Setting Up Angular

Use the following to instantiate your Angular app. var app = angular.module('FavoriteFoods');

Hint: Controller

Use the following to register a controller to your app. app.controller('MainController', function($scope) { // some code };

Iterating Using ngRepeat

Using $scope

Now that our app is all ready, create an array of objects of the information you gathered in the beginning. Here is an example of what your array might look like:

[
    {
        name: 'Ben',
        foods: ['pizza', 'burritos', 'fried chicken']
    },
    {
        name: 'Colin',
        foods: ['lemons', 'oranges', 'limes']
    },
    {
        name: 'Wesley',
        foods: ['sandwiches', 'cauliflower', 'cottage cheese']
    },
    {
        name: 'Sam',
        foods: ['goat cheese', 'blue cheese', 'cream cheese']
    },
    {
        name: 'Carlos',
        foods: ['red peppers', 'green peppers', 'yellow peppers']
    }
]

Using the $scope that you injected into your controller, assign this array to a scope variable called people. After creating the scope property, interpolate this variable in your HTML file. If you don't remember how to do this, check the hint.

Hint: Interpolating

You can interpolate variables using double curly braces. The value/express between the braces will be run as JavaScript.

Creating a List

So we can see that we can access the array in the HTML, but it's still in array form. Let's try iterating over the array so that we can get to each element individually. We can do this using Angular's ngRepeat directive.

The way that ngRepeat works is similar to the array methods of .map and .forEach. As it iterates over the array, it assigns each element a variable that you can use to reference that element. Below is an example of ngRepeat in action:

<ul>
    <li ng-repeat="article in articles">{{ article.title }}</li>
</ul>

// This will become...

<ul>
    <li>My First Article</li>
    <li>My Second Article</li>
    <li>My Third Article</li>
    . . .
</ul>
```
In the `<ul>` tag, the `ng-repeat` attribute on the `<li>` tag is used to iterate over an array called `articles`. The tag that the ngRepeat attribute is called on will be the tag that gets repeated; in this example, the `<li>` tag will be repeated for each element in the `articles` array. We are assigning each element in `articles` to the variable `article` and then accessing the `title` property on each article using the double curly braces.

Try creating a similar list of the names you collected.

### Inception-Mode
After printing out the names of the people, we still have some unused data--their favorite foods. You can nest ngRepeats as much as you would like. Use ngRepeat to create a list of favorite foods for each person in your list of people.

*Hint: Lists in Lists*

*You can nests `<ul>`'s like so:*
```
<ul>
    <li ng-repeat="item in items">
        <ul>
            <li ng-repeat="thing in things">
        </ul>
    </li>
</ul>
```
Once you list out each person's favorite foods in the list of people, you're done! Now you're an ngRepeat master!

**Bonus: ngRepeat doesn't just work for array, you can also iterate over objects. If you want to learn more about ngRepeat, check out the Angular docs: https://docs.angularjs.org/api/ng/directive/ngRepeat**

Ng-repeat : An introduction

What is ng-repeat?

As you may remember AngularJS lets you extend HTML with new attributes called Directives.

AngularJS directives are extended HTML attributes with the prefix ng-.

For example, the ng-app directive initializes an AngularJS application. Another very common one is ng-model, a directive that binds the value of HTML controls (input, select, textarea) to application data. There are many directives and eventually you will create your own soon (yeah!! we will teach you, don't worry), but for the moment we are going to explore one of the most powerful, ng-repeat. The ng-repeat directive is very handy, and allows you to repeat elements on the view based on a collection.

Let's see a little code and from there we will move on. First example: repeating an HTML element

<div ng-app="" ng-init="names=['Jani','Hege','Kai']">
  <ul>
    <li ng-repeat="x in names">
      {{ x }}
    </li>
  </ul>
</div>

In this case we can see that the x inside {{ x }} is bound to the array names declared on ng-init. If we render this on our browser, we will see something like:

Looping with ng-repeat:

  • Jani
  • Hege
  • Kai

Second example: usage over an array of objects

<div ng-app="" ng-init="names=[
{name:'Jani',country:'Norway'},
{name:'Hege',country:'Sweden'},
{name:'Kai',country:'Denmark'}]">

<ul>
  <li ng-repeat="x	in names">
    {{ x.name + ', ' + x.country }}
  </li>
</ul>
</div>

Here, we are working with an array of objects, each object with two properties: name and country. The output will be something like:

Looping with objects:

  • Jani, Norway
  • Hege, Sweden
  • Kai, Denmark

As you may have noticed this is a very powerful tool, avoiding us a lot of work. Just using one <li> tag with the ng-repeat directive we are generating three <li> tags, one for each element in the array, with the particular data value for each individual inside. Awesome!!

But the magic only starts here. The true power of this directive is not to loop over elements defined by us in the same HTML page. The power comes when we bind it to data that comes from another sources like our controllers.

Interacting with our controllers:

Let's say you have a factory that provides you with an array of elements that contains the cast of the movie Fight Club. Something like:

var myApp = angular.module('myApp', []);

myApp.factory('Fight_Club', function() {
  var Fight_Club = {};
  Fight_Club.cast = {
   { name: "Brad Pitt",
     character: "Tyler Durden"
   },
   { name: "Edward Norton",
     character: "The narrator"
   },...

And a basic controller:

function FightClubCtrl($scope, Fight_Club) {
  $scope.fight_club = Fight_Club;
)

Now we can show this data dynamically using the following:

<div ng-controller="FightClubCtrl">
  <table>
    <tr ng-repeat="actor in fight_club.cast">
      <td>{{actor.name}}</td>
      <td>{{actor.character}}</td>
    </tr>
  </table>
</div>

This will show us a list with all the cast of Fight Club. Name and character. One <tr> and two <td> for each one. Cool! Isn't it?

Notice we used actor, but we could have chosen any other name. In this case is just a variable that identifies a individual element inside the collection we are iterating on. The in is used to introduce the data we are going to iterate on.

Now we are going to see something that makes this even powerful: filters!

Filters

In many situations we don't want to show the whole data in our collection. Maybe we only want to show the data that contains one particular name like "Brad" or data that fits another criteria. For these cases we have filters, a feature built inside ng-repeat. Let's see how they work incorporating one to our previous example:

<div ng-controller="FightClubCtrl">
  <input type="text" ng-model="searchText">
  <table>
    <tr ng-repeat="actor in fight_club.cast | filter:searchText">
      <td>{{actor.name}}</td>
      <td>{{actor.character}}</td>
    </tr>
  </table>
</div>

Notice we have made two changes:

  • Line 2: Defined a ng-model "searchText" on an input element.
  • Line 4: Attached a filter referring to that ng-model "searchText"

Modify your code to include this and be ready to be amazed. Once done try typing in the input element just created the string "Br". You will see that now, your list only includes the elements with "Br" on them, either on the name or in the character properties. As you type it is filtering by that value! Dynamically! That is the power of AngularJS and its Data Binding.

If you just want it to filter by a specific property like name, then we have to make two small changes:

<div ng-controller="FightClubCtrl">
  <input type="text" ng-model="search.name">
   <table>
    <tr ng-repeat="actor in fight_club.cast | filter:search">
     <td>{{actor.name}}</td>
     <td>{{actor.character}}</td>
    </tr>
  </table>
</div>

Notice we have made two changes:

  • Line 2: "searchText" for "search.name". We specify here the property the ng-model is attached to.
  • Line 4: "searchText" for just "search"

Now you can see it will only filter by the property name. If you change "search.name" for "search.character", the filtering will be done over that field only.

If you the case you want to make it filter by whichever property like in the first example, you can also use "search.$" instead of "search.name".

And that is a basic introduction to ng-repeat. Hope you have enjoyed it!!

Terminology and Concepts

The below terminology is used in the workshop. ####Directive Directives is not the focus of this workshops, however it is important to understand the term. As an analogy, as everything in JavaScript is an Object, everything in Angular is a Directive (ng-something). You can create your own directives, however, we will be using Angulars ng-Repeat directive for this workshop. ####Controller and Template Relationship The relationship between the template (html file) and the controller (script/javascript files) is the management of the $scope object whereby the controller instantiates scope items which the controller can update and can be updated via the template (ng-model directive). ####$scope ######Controller In the controller the $scope object will always be referenced as:

$scope.variableName

######Template In the template the object will always be refered to as

variableName 

where $scope. is implied, not typed.

Code Angels: This is a good starting point, however, feel free to raise a help-ticket to clarify your understanding of the concepts.

Ready

ng-Repeat is a attribute directive that is used on templates for looping over a collection of data that is provided by the controller on the $scope object. Note that a collection can be either an array or object.

Set

For this workshop, we are going to use a sandbox environment to test and learn things out about ng-Repeat. Head over to this link to get started.

Wealthy Coder Tip: Try your best to type up the code for learning purposes and practice (muscle memory). If you are just copy and pasting, you are replaceable by AI Code Bots!!

Code!

Each concept is an independent code snippets that do not rely on each other. Let's start coding!

#####Concept 1: Individual $Scope Type the following in the HTML panel anywhere within the body tags.

<button ng-repeat="n in [41, 42, 43, 44]" ng-click="isClicked()">
    {{n}}
</button>```
And the following in your backend TestCtrl controller:
``` sh 
$scope.isClicked = function(){
    console.log("I was clicked!");
}

Using the Chrome Developer Tools (or your favorite browser), observe what is actually being generated by Angular on the HTML and in the Console (trying clicking each button).

Code Angels: Raise a help ticket to get a quick intro to using the Browser Developer Tools if you are not sure.

The actual HTML output is as follows:

<button ng-repeat="n in [41, 42, 43, 44]" ng-click="isClicked()" class="ng-scope ng-binding">41</button>
<button ng-repeat="n in [41, 42, 43, 44]" ng-click="isClicked()" class="ng-scope ng-binding">42</button>
<button ng-repeat="n in [41, 42, 43, 44]" ng-click="isClicked()" class="ng-scope ng-binding">43</button>
<button ng-repeat="n in [41, 42, 43, 44]" ng-click="isClicked()" class="ng-scope ng-binding">44</button>

And the console log is as follows:

2015-05-14 19:46:14.632 pen.js:pen.js:6 I was clicked!
2015-05-14 19:46:14.681 pen.js:6 I was clicked!
2015-05-14 19:46:15.389 pen.js:6 I was clicked!

Code Talk: Can you explain to your partner or fellow-coders what is actually going on before moving on? ######What is ng-Repeat Doing?

In this example, ng-repeat is creating a <button> for each item in the collection, and each individual items receives its own individual scope. ######Why is this important? When each button is creating its individual scope, it can become challenging to identify the actual scope where an event occurs.

######Code Challenge 1: Can you modify the code (template and controller) to identify which button is being clicked? Let's say we want the Console to show the button number in the console as a rudimentary example. Give it a try before continuing to look at the solution.

######Code Challenge 1 Solution: Below is an example solution where you are passing in the actually value to the ng-click directive expression (isClicked(n)) Template

<button ng-repeat="n in [41, 42, 43, 44]" ng-click="isClicked(n)">{{n}}</button>

Controller

$scope.isClicked = function(n){
    console.log(n + " was clicked!");
}

######Code Challenge 2: How would you actually show the index that is being selected? Slight variation to your template code. Try it out before moving forward. ######Code Challenge 2 Solution:

<button ng-repeat="n in [41, 42, 43, 44]" ng-click="isClicked($index)">{{n}}</button>

$index is a property that is available with ng-repeat to access the index of the current item in the collection. #####Concept 2: Collections as Objects Type the following in the HTML panel anywhere in the body tags.

<div ng-repeat="(key, value) in {a:1,b:2,a:3,d:4}"> {{value}}</div> 

Do you notice that only three of the four items are printed? By default ngRepeat** does not allow for duplicates in objects (there are two a keys in the object) **, so it is best to convert to an array to track each item using the $index property. The best way around this is to convert the object to an array (this is left as an extra credit).

Resources

Visit the angular documentation for further reading.

#Ng-repeat# ng-repeat is a built-in Angular directive that we can use to markup anything we want to repeat. Let's see how we can use this to make our lives greater :)

##Set-up Fork from this git repo https://github.com/sanghun89/fellowapp.git, clone, then work inside the app.js and index.html to produce your result.

(Note To instructors: Not sure how to show and hide content on markdown, but my "solution" parts are the togglers for hints.)


##Using ng-repeat ###Exercise 1: Getting Started Copy the javascript array in this gist and create the following list:

  • ALABAMA
  • ALASKA
  • ARIZONA
  • ARKANSAS
  • CALIFORNIA
  • COLORADO
  • CONNECTICUT
  • DELAWARE
  • DISTRICT OF COLUMBIA
  • FLORIDA (...all the way to WHYOMING)

###Exercise 1 Solution After copying the data in your app.js, in your index.html, you simply just need to put something like this! Magic!

<ul>
    <li ng-repeat="state in usStates">{{state.name}}</li>
</ul>

###Exercise 2: index..? Okay that was kind of cool, but let's do some cooler stuff. On Each list, let's have a button for each state, in which upon click will add a string, "is the best!". As a hint, for this exercise, you might want to take a look at using $index, and ng-click.

###Exercise 2 Solution You can think ng-repeat almost like your vanilla javascript's for loop. $index will keep getting incremented (starting at zero), and on each iteration of repeated block code, you can use $index as a context for anythign inside the block, such as the function we would have to make. Here is my version of the solution:

//index.html
<li ng-repeat="state in usStates">
    {{state.name}}
    <button ng-click="addCool($index)">Add Cool</button>
</li>

//app.js
// inside NgRepeatController
$scope.addCool = function(index) {
    $scope.usStates[index].name += ' is the best!'
};

###Exercise 3: Make yo state! If you haven't got familiar with ng-model, and, For the purpose of the exercise, just know that when you create something like this:

<input type="text" ng-model="newState">

And you should intialize this "ng-model" in your controller by your $scope like this:

$scope.newState = '';

Think of ng-model as something that allows you to tie user inputs to your scope. Now that you know, create a input text field with a button that will add your NEW string in all CAPITAL letters, as the FIRST thing in your list of states. Fire an alert if there are no strings being passed.

###Exercise 3: Solutions Here is my version of the magic:

// index.html
Search your state:
<input type="text" ng-model="newState">
<button ng-click="addNewState(newState)">Add State</button>
<ul>
    <li ng-repeat="state in usStates">
        {{state.name}}
        <button ng-click="addCool($index)">Add Cool</button>
    </li>
</ul>

//app.js
$scope.newState = '';
$scope.addNewState = function(state) {
    if (state === '') return alert("enter a new state!");

    $scope.usStates.unshift({
        name : state.toUpperCase()
    });
};

You see how you only have to deal with your actual data in the controller, and your html will automatically reflect that?! This angular's magic is possible by running something what they call $digest() when angular actions have been fired.


###Exercise 4: Search yo state! Similar to the previous exercise, you will still have an input field, but no buttons this time. However, as you type in your input field, make it so that the list FILTERS through and reflect similar matches to what you have typed. For example, if I type "Georgia", there will only be one list with Georgia, but if I have only typed "N", the list will only have states that contains the letter "N". Go! Take a look at filters. The first example should give you a hint!

###Exercise 4: Solutions In this exercise, you didn't even had to touch a single thing app.js. Here is my solution:

Search your state: <input type="text" ng-model="search">
<ul>
    <li ng-repeat="state in usStates | filter : search">
        {{state.name}}
    </li>
</ul>

Mix of filters and ng-repeat works wonders. You can assign filters to be many things, and our case is pointing at the ng-model "search" we created. If you had to think about what it would take for jQuery to do something like this, and angular can handle with just writing in HTML!

Now that you have a feel for ng-repeat, you can go explore what else ng-repeat is good for. One of the use cases is in the extra credit! :)


###Exercise 5: Extra Credit As a web developer, one thing you will always have an encounter to is form handling. It is absolutely dreadful to handle forms in the past, but by having Angular on your side, form handling has become WAY, WAY better. Ng-repeat does contribute to making your "form" life better, and here is one of the ways in this exercise.

By using the same state array, make a html SELECT tag with options containing all the states. The option should display the actual name of the states, but holds only the abbreviated version of the state as the value. Upon selecting a state, please show the abbreviated state chosen in whatever fashion you want it to display. Also, at the start of the page, make sure that "Georgia" is selected on default, because that's where I'm from! The most creative way of displaying the state on select will get a prize from me if I become a fellow! :D

###Exercise 5: Solutions There are other ways to do this, here is the solution using ng-repeat:

// index.html
<select ng-model="chosenState">
    <option ng-repeat="state in usStates"
    value="{{state.abbreviation}}"
    ng-selected="state.abbreviation === 'GA'">
        {{state.name}}
    </option>
</select>
<p>{{chosenState}}</p>

One thing new about this one, other than changing the HTML markup, is ng-selected. You can insert any kind of javascript expression to validate is the current option on the list is going to be selected or not. As you can also see, all this repeating the list and dealing with what gets selected can be stated in your HTML mark-up! HOW COOL IS THAT!?

Thanks for going through the workshop, and I hope ng-repeat is no longer something to be feared by you! :D

##Overview

In Jquery, after fetching information from the server- if you wanted to display it to the DOM you might append it to an HTML container, perhaps a

. In angular we have a very different approach to this problem, which re-imagines the role of HTML in writing front-end code.

Traditionally, we think of HTML as static, and not doing anything in particular. However Angular re-considers the HTML view to be capable of lots of things. This power is given to something in Angular called a 'Directive'. The directive encourages programmers to keep all of the view logic contained to the view; encouraging cleaner, more structured and maintainable code.

In this workshop we will look at built-in angular directive Ng-Repeat which allows you to loop through data on your controller and display it out in order, on the view.

##NG-Repeat

In order to take advantage of Ng-Repeat we will need to build an application in Angular. First, we need to create an angular module which defines the angular application. This module acts as a container for the application. I would suggest copying this code into your text editor and following along


//in app.js
    
    var app = angular.module("myApp", []);

We then need to create a controller. The controller's main role is to attach functions and properties to the scope. The HTML then binds to these properties and shows them in the view.

In the example below you can see we have also added an array of num objects to the scope. In practice this information might be retrieved from a Service or AJAXed from a database or external API.


//in app.js

   app.controller('BasicController', function($scope) {
        $scope.items =  [{num: 1}, {num:2}, {num:3}, {num:4}];
    });

Our HTML at this point might look like this



    
  
    

      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
  <script src="app.js"></script>
</head>
<body>
  <div ng-controller="BasicController">
      {{items}}
  </div>
</body>

If you load this file in your browser you should see the items that we have bound to the scope of the controller appearing on the page. Part of the 'magic' of angularJS is that it knows how to check the scope of the controller for the data we want attached in our HTML.

How might we get a list of these items to display? You might think to do something like this


    
  • {{items[0].num}}
  • {{items[1].num}}
  • {{items[2].num}}
  • {{items[3].num}}

But this involves repetitive code, and lacks flexibility when it comes to arrays of arbitrary length. This is where the powerful built-in directive ng-repeat really shines. We can refactor the above code to this:


  
  • {{item.num+" at index "+$index}}

Ng-repeat is an attribute directive. Which means it acts and behaves like an HTML attribute, with its own, custom functionality.

It works by creating an HTML template once per iteration of the loop. Each template is given its own scope - a 'child' scope; in this example it is prototypically inhereting from the BasicController parent scope.

We have provided a given loop variable. In this case, the loop variable is 'item' which is set to the current item in the collection or list. $index variable will give you the current index number in the loop collection.

Give this a shot yourself. Try creating a table with these values:

$scope.classMembers = 
[{Name: "Sam", Country: "Australia"}, 
{Name: "Ben", Country: "China"},
{Name: "John", Country: "USA"},
{Name: "Lucy", Country: "France"},
{Name: "Mary", Country: "Canada"},
{Name: "Joe", Country: "USA"}];

Basic Solution:

  <table>

  <tr ng-repeat="person in classMembers">
    <td>{{ $index + 1 }}</td>
    <td>{{ person.Name }}</td>
    <td>{{ person.Country }}</td>
  </tr>
</table>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment