commit
890255fa21
@ -0,0 +1,51 @@
|
|||||||
|
# Javascript - Cross Site AJAX
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Explain Cross Site AJAX
|
||||||
|
1. Explain CORS
|
||||||
|
1. Explain Proxy Server
|
||||||
|
1. Explain JSONP
|
||||||
|
|
||||||
|
## Explain Cross Site AJAX
|
||||||
|
|
||||||
|
By default it's considered insecure to make an AJAX request to a different site
|
||||||
|
```javascript
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: 'https://status.github.com/api/status.json',
|
||||||
|
}).then(
|
||||||
|
function(response){
|
||||||
|
|
||||||
|
},
|
||||||
|
function(response){
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explain CORS
|
||||||
|
|
||||||
|
- new way of doing this
|
||||||
|
- in headers of html, server decides who can access this data
|
||||||
|
- if correct headers are present, browser knows it's secure to view the data
|
||||||
|
1. http://www.omdbapi.com/?s=pirates&r=json
|
||||||
|
1. `Access-Control-Allow-Origin:*`
|
||||||
|
|
||||||
|
## Explain Proxy Server
|
||||||
|
|
||||||
|
- have your sever make a request to the foreign API and then return the results
|
||||||
|
- you can then hit your own API and it will be the same origin
|
||||||
|
|
||||||
|
## Explain JSONP
|
||||||
|
|
||||||
|
- old way of getting around this
|
||||||
|
- add a script tag to the service
|
||||||
|
- pass your custom callback function in as a GET param
|
||||||
|
- inside other site's script tag, it calls your function and passes in its data
|
||||||
|
```html
|
||||||
|
<!-- Request sent via a script tag -->
|
||||||
|
<script src="https://status.github.com/api/status.json?callback=apiStatus"></script>
|
||||||
|
<!-- Data received during execution of this predefined function. -->
|
||||||
|
<script> function apiStatus(data) { console.log(data); } </script>
|
||||||
|
```
|
||||||
@ -0,0 +1,191 @@
|
|||||||
|
# Javascript - AJAX
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
1. Explain AJAX
|
||||||
|
1. Demonstrate jQuery AJAX
|
||||||
|
1. Explain promises
|
||||||
|
1. Explain creating your own promises
|
||||||
|
|
||||||
|
## Explain AJAX
|
||||||
|
- stands for Asynchronous JavaScript and XML
|
||||||
|
- No longer uses XML. Uses JSON
|
||||||
|
- The browser, using JavaScript makes a request without reloading current page
|
||||||
|
- normally, the user makes a request by clicking a link/typing in a URL
|
||||||
|
|
||||||
|
## Demonstrate jQuery AJAX
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$.ajax({
|
||||||
|
url:'http://www.omdbapi.com/',
|
||||||
|
method: "GET",
|
||||||
|
data: { s: "star wars" }, //data that gets passed to server
|
||||||
|
success: function(data, status, jqXHR){ //success callback
|
||||||
|
console.log(data); //just dumps data to console, but you can do more here
|
||||||
|
},
|
||||||
|
error: function(jqXHR, status, error){ //error callback. Gets called on situations like trying to hit a url that doesn't exist
|
||||||
|
console.log('bad');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explain promises
|
||||||
|
|
||||||
|
- a way to organize your code better
|
||||||
|
- no added functionality, just easier to read
|
||||||
|
- $.ajax(...) returns a promise object
|
||||||
|
- this object has functions on it for when the ajax completes, depending on state
|
||||||
|
- done (successfully completes)
|
||||||
|
- fail (error encountered)
|
||||||
|
- always (either error or success)
|
||||||
|
- pass in a callback function to be executed when the various situations arise
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
const promise = $.ajax({ //promise gets returned.
|
||||||
|
url:'http://www.omdbapi.com/',
|
||||||
|
method: "GET",
|
||||||
|
data: { s: "star wars" }
|
||||||
|
// no properties inside this object for success, error
|
||||||
|
});
|
||||||
|
promise.done(function(data){ //instead the callbacks are defined here
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
|
promise.fail(function(data){ //and here
|
||||||
|
console.log('fail!');
|
||||||
|
});
|
||||||
|
promise.always(function(data){ //this will always run whether on success or failure
|
||||||
|
console.log('always!');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
- the `done()`, `fail()`, and `always()` functions return the same promise object
|
||||||
|
- you can use this returned promise object to chain your callbacks onto each other
|
||||||
|
- done can contain multiple callbacks
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$.ajax({
|
||||||
|
url:'http://www.omdbapi.com/',
|
||||||
|
method: "GET",
|
||||||
|
data: { s: "star wars" }
|
||||||
|
})
|
||||||
|
.done(function(data){ //using some fancy chaining here
|
||||||
|
console.log(data);
|
||||||
|
}, function(data){ //a second callback is executed on success after the one above
|
||||||
|
console.log('another done');
|
||||||
|
})
|
||||||
|
.fail(function(data){
|
||||||
|
console.log('fail!');
|
||||||
|
})
|
||||||
|
.always(function(data){
|
||||||
|
console.log('always!');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
- use `.then()` as a shorthand function which takes two function callbacks as parameters
|
||||||
|
- 1st param is the success callback
|
||||||
|
- 2nd param is the error callback
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$.ajax({
|
||||||
|
url:'http://www.omdasdfbapi.com/',
|
||||||
|
method: "GET",
|
||||||
|
data: { s: "star wars" }
|
||||||
|
})
|
||||||
|
.then(function(data){ //success function here
|
||||||
|
console.log(data);
|
||||||
|
}, function(data){ //error function here
|
||||||
|
console.log('fail!');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explain creating your own promises
|
||||||
|
- `$.Deferred();` returns a deferred object
|
||||||
|
- a deferred object can create a promise
|
||||||
|
- it can also successfully resolve that promise or have it error out
|
||||||
|
- you can then `resolve()` or `reject()` these promises whenever you want
|
||||||
|
- use `$.when` to test when multiple promises are resolved
|
||||||
|
- success when all succeed
|
||||||
|
- failure when one or more fail
|
||||||
|
- could do this using nested promises
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
<script type="text/javascript" src="jquery-1.11.3.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
const deferred = $.Deferred(); //deferred is kind of like a factory that generates promises
|
||||||
|
const promise = deferred.promise(); //creating a promise here
|
||||||
|
setTimeout(function(){ //create a timeout, then resolve the promise after one second
|
||||||
|
deferred.resolve("first promise"); //use the deferred object to have the promise succeed
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
const deferred2 = $.Deferred(); //creating a second "promise factory". Each deferred object can create/resolve only one promise
|
||||||
|
const promise2 = deferred2.promise(); // creating a second promise
|
||||||
|
setTimeout(function(){ //making this promise error out after 5 seconds
|
||||||
|
deferred2.reject("second promise");
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
promise.fail(function(value) { //first promise callbacks defined here
|
||||||
|
console.log('fail first: ' + value);
|
||||||
|
}).always(function(value){
|
||||||
|
console.log('always first: ' + value);
|
||||||
|
}).done(function(value){
|
||||||
|
console.log('done first: ' + value);
|
||||||
|
});
|
||||||
|
|
||||||
|
$.when(promise, promise2) //these callbacks are invoked once both promise1 and promise2 return
|
||||||
|
.done(function(value1, value2){ //success of both
|
||||||
|
console.log('done both: ' + value1 + ", " + value2);
|
||||||
|
}, function(value1, value2){ //an optional second success callback
|
||||||
|
console.log('2nd done both: ' + value1 + ", " + value2);
|
||||||
|
})
|
||||||
|
.then(function(value1, value2){ //using a then to create success and error callbacks together
|
||||||
|
console.log('then both success: ' + value1 + ", " + value2);
|
||||||
|
}, function(value1, value2){
|
||||||
|
console.log('then one/both fail: ' + value1 + ", " + value2);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
@ -0,0 +1,237 @@
|
|||||||
|
# AngularJS - basics
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Describe Single Page Apps and Why They're Great
|
||||||
|
1. Describe what Angular.js does for us
|
||||||
|
1. Describe two way data binding
|
||||||
|
1. Set up Angular
|
||||||
|
1. Explain Directive
|
||||||
|
1. Describe Applications
|
||||||
|
1. Describe Controllers
|
||||||
|
1. List common directives
|
||||||
|
|
||||||
|
## Describe Single Page Apps and Why They're Great
|
||||||
|
- load one larger file for html/css/js which will run entire site
|
||||||
|
- is faster than loading many smaller html/css/js files for each page
|
||||||
|
- if application is rendered on client side, it cuts down on the work needed by the server
|
||||||
|
- server can run faster because there is less traffic to it
|
||||||
|
- most people's computers can handle running the JS required for a single page application
|
||||||
|
|
||||||
|
## Describe what Angular.js does for us
|
||||||
|
|
||||||
|
- Allows us to structure our files in an MVC structure, so we'll have data, controllers, and html
|
||||||
|
- this enforces a structure to our application that makes it easy to figure out where to make updates and keeps the code clean
|
||||||
|
- Simplifies process of updating the HTML based on data
|
||||||
|
- remember in the games for Unit 1, when the JS data changed, we had to manually tell the HTML to update as well
|
||||||
|
- with Angular, HTML updates whenever the data updates (two way data binding)
|
||||||
|
|
||||||
|
## Describe two way data binding
|
||||||
|
- Javascript Models are synchronized with HTML elements
|
||||||
|
- When properties in the model get updated, so does the UI.
|
||||||
|
- When UI elements get updated, the changes get propagated back to the model.
|
||||||
|
- Ex: in connect four, could have an array of arrays (matrix).
|
||||||
|
- When a value is changed in the matrix, the HTML automatically shows a token on the board with no further coding
|
||||||
|
|
||||||
|
## Set up Angular
|
||||||
|
|
||||||
|
Add a script tag referencing the Angular library, just like when we used jQuery
|
||||||
|
|
||||||
|
file:index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Explain Directive
|
||||||
|
|
||||||
|
1. HTML has pre-defined attributes on tags like `href`, `src`, `class`
|
||||||
|
1. You can come up with your own attributes as well: `<div matt="awesome"></div>`
|
||||||
|
1. Angular uses its own custom directives to tell the HTML which data it is bound to
|
||||||
|
- they all start with `ng-`
|
||||||
|
|
||||||
|
## Describe Applications
|
||||||
|
|
||||||
|
1. `ng-app="MyApp"`
|
||||||
|
- our first directive, placed probably in `<html>`
|
||||||
|
1. `const app = angular.module('MyApp', []);`
|
||||||
|
- creates our application, which can hold many Controllers
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="MyApp">
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
|
||||||
|
<script src="app.js"></script><!-- add reference to controller js file -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Describe Controllers
|
||||||
|
|
||||||
|
1. Controllers control a certain area of the page
|
||||||
|
```javascript
|
||||||
|
app.controller('MyController', [function(){ //constructor function called MyController
|
||||||
|
this.hello = 'oh hai!';
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
1. `ng-controller = "MyController as ctrl"`
|
||||||
|
- instantiates MyController as an object with name `ctrl`
|
||||||
|
- similar to `const ctrl = new MyController();`
|
||||||
|
- can be placed on any element that is inside the element that has `ng-app` set
|
||||||
|
- anything inside that element has access to anything on `ctrl` object
|
||||||
|
|
||||||
|
file:index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="MyApp">
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-controller="MyController as ctrl">
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file:app.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('MyController', [function(){ //constructor function called MyController
|
||||||
|
this.hello = 'oh hai!';
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Directives
|
||||||
|
|
||||||
|
### reading values
|
||||||
|
|
||||||
|
1. ng-bind
|
||||||
|
- binds the contents of the element to whatever is specified
|
||||||
|
1. {{}}
|
||||||
|
- same as ng-bind, but not an attribute
|
||||||
|
1. ng-class
|
||||||
|
- binds the class of an element to the result of an "expression"
|
||||||
|
- an expression is just some code that gets evaluated
|
||||||
|
- `(ctrl.isActive)?'active':''"`
|
||||||
|
- if ctrl.isActive evaluates to true, the class of the element will be 'active'
|
||||||
|
- `ng-class="{ active:ctrl.isActive }"`
|
||||||
|
- a different way to write the same thing
|
||||||
|
1. ng-repeat
|
||||||
|
- `<li ng-repeat="item in ctrl.items">{{item}}</li>`
|
||||||
|
- loops through an array, creating duplicates of the element for each value in the array
|
||||||
|
- can reference the current item, based on whatever value is given before `in`
|
||||||
|
- use `{{$index}}` to get the index of the element in the array
|
||||||
|
- ng-repeat doesn't like duplicate items in the array (`ng-repeat="element in [1,1,1]"`)
|
||||||
|
- use `track by $index` to get around this
|
||||||
|
- `ng-repeat="element in [1,1,1] track by $index"`
|
||||||
|
1. ng-if
|
||||||
|
- `ng-if="ctrl.divExists"`
|
||||||
|
- binds the existence of an element to the result of an expression
|
||||||
|
1. ng-show/hide
|
||||||
|
- like ng-if, but it hides it, not removes it from the DOM
|
||||||
|
1. src
|
||||||
|
- `<img src="{{ctrl.imgsrc}}" />`
|
||||||
|
- sets the src of an img to the result of an expression
|
||||||
|
|
||||||
|
file:index.html
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="MyApp">
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-controller="MyController as ctrl">
|
||||||
|
<div ng-bind="ctrl.hello"></div><br/>
|
||||||
|
{{ctrl.hello}}
|
||||||
|
<section ng-class="(ctrl.isActive)?'active':''"></section>
|
||||||
|
<ul>
|
||||||
|
<li ng-repeat="item in ctrl.items">{{$index}}: {{item}}</li>
|
||||||
|
</ul>
|
||||||
|
<div ng-if="ctrl.divExists">Remove Me</div>
|
||||||
|
<div ng-hide="ctrl.hideDiv">Hide Me</div>
|
||||||
|
<div ng-show="ctrl.showDiv">Show Me</div>
|
||||||
|
<img src="{{ctrl.imgSrc}}" />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('MyController', [function(){ //constructor function called MyController
|
||||||
|
this.hello = 'oh hai!';
|
||||||
|
this.isActive = true;
|
||||||
|
this.items = ['apple', 'banana', 'pear'];
|
||||||
|
this.divExists = false;
|
||||||
|
this.hideDiv = true;
|
||||||
|
this.showDiv = false;
|
||||||
|
this.imgSrc = 'http://1.bp.blogspot.com/-CzqzzBV2tMk/TxBM3ar18MI/AAAAAAAAPm0/6faLPO9BM8w/s1600/i-can-has-cheezburger.jpg';
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### writing values
|
||||||
|
|
||||||
|
1. ng-model
|
||||||
|
- `<input ng-model="ctrl.tab"/>`
|
||||||
|
- binds ctrl.tab's value to whatever is the value of the input
|
||||||
|
1. ng-click
|
||||||
|
- `<button ng-click="ctrl.tab = 4">Click Me</button>`
|
||||||
|
- executes code when an element is clicked
|
||||||
|
1. ng-submit
|
||||||
|
- executes code when form is submitted
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="MyApp">
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-controller="MyController as ctrl">
|
||||||
|
{{ctrl.someProperty}}
|
||||||
|
<form ng-submit="ctrl.handleSubmit()">
|
||||||
|
Change the property's value: <input type="text" ng-model="ctrl.someProperty"/><br/>
|
||||||
|
<input type="submit" value="Submit The Form"/>
|
||||||
|
</form>
|
||||||
|
<div ng-click="ctrl.handleClick()">Click me</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: app.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('MyController', [function(){ //constructor function called MyController
|
||||||
|
this.someProperty = 'foo';
|
||||||
|
this.handleSubmit = function(){
|
||||||
|
console.log('form submitted');
|
||||||
|
}
|
||||||
|
this.handleClick = function(){
|
||||||
|
console.log('div clicked');
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
```
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
# AngularJS - Nesting, Services
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Nest controllers within each other
|
||||||
|
1. Use services to add functionality to controllers
|
||||||
|
|
||||||
|
## Nest controllers within each other
|
||||||
|
|
||||||
|
1. can have controllers nested within other controllers
|
||||||
|
1. those inner controllers can access the properties of their parents
|
||||||
|
1. useful for separating complex sections into smaller components
|
||||||
|
1. Batarang
|
||||||
|
- a chrome plugin that allows you to inspect the properties of controllers defined on elements
|
||||||
|
1. add to Chrome and enable it
|
||||||
|
1. Click on an element in the Chrome Dev tools
|
||||||
|
1. There is now a $scope tab
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="MyApp">
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div ng-controller="ParentController as parent">
|
||||||
|
<div ng-controller="ChildController as child">
|
||||||
|
<span>{{child.property1}}:{{parent.property1}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: app.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('ParentController', [function(){
|
||||||
|
this.property1 = 'fun';
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('ChildController', [function(){
|
||||||
|
this.property1 = 'awesome';
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use services to add functionality to controllers
|
||||||
|
|
||||||
|
1. Services provide additional functionality to your controller
|
||||||
|
- names always start with $
|
||||||
|
- examples
|
||||||
|
- $scope
|
||||||
|
- handles how view receives data
|
||||||
|
- $log
|
||||||
|
- logging
|
||||||
|
- $http
|
||||||
|
- handles AJAX
|
||||||
|
1. Need to be passed in as parameters of controller
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.controller('SomeController', [ '$someService', function($someService){
|
||||||
|
$someService.doStuff();
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Array syntax with `$someService` string avoids problems later on with minifying your code
|
||||||
@ -0,0 +1,226 @@
|
|||||||
|
# AngularJS - AJAX
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Describe AJAX
|
||||||
|
1. Use the $http module to make an AJAX request
|
||||||
|
1. Use the response from an AJAX request to update the DOM
|
||||||
|
1. Alter express bodyParser to accept AJAX requests
|
||||||
|
1. Use cURL to send JSON
|
||||||
|
|
||||||
|
## Describe AJAX
|
||||||
|
|
||||||
|
- AJAX stands for Asynchronous JavaScript and XML, but we mostly use JSON instead of XML now.
|
||||||
|
- With AJAX, the browser uses JavaScript to make requests to servers and parse the responses after a page has loaded.
|
||||||
|
- This means data can be retrieved and the web page updated without needing to reload the entire page each time an update needs to happen.
|
||||||
|
- This makes the application feel smooth and fast and much more like a native app, as opposed to a series of web pages that a user visits
|
||||||
|
|
||||||
|
## Use the $http module to make a request
|
||||||
|
|
||||||
|
- `$http` is a service which needs to get passed into the controller.
|
||||||
|
- Inside the controller constructor function, `$http` is a function that makes a request to a server.
|
||||||
|
- It takes an object as a parameter. This object can have many parameters which affect the request.
|
||||||
|
- `$http` returns a "promise" object.
|
||||||
|
- On this object is a `then` property, which is a function.
|
||||||
|
- You pass success and fail callbacks into this `then` function as parameters
|
||||||
|
- These callbacks will be executed depending on what kind of response is received from the server
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('myApp', []);
|
||||||
|
|
||||||
|
app.controller('baseCtrl', ['$http', function($http){
|
||||||
|
this.find = function(){ //this function will get called somewhere in the DOM
|
||||||
|
$http({ //once it's called, make an AJAX call to OMDB
|
||||||
|
method: 'GET',
|
||||||
|
url: 'http://www.omdbapi.com/?t=star+wars&y=&plot=short&r=json&apikey=bea723f3'
|
||||||
|
}).then(
|
||||||
|
function(response) {
|
||||||
|
//success callback
|
||||||
|
console.log(response);
|
||||||
|
},
|
||||||
|
function(response) {
|
||||||
|
//failure callback
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
we can now call the find function
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<div ng-controller="baseCtrl as base">
|
||||||
|
<a ng-click="base.find()">Click me</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use the response from an AJAX request to update the DOM
|
||||||
|
|
||||||
|
- Inside the callback function for the AJAX request, the `this` keyword has been changed to browser window object
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
$http({ //once it's called, make an AJAX call to OMDB
|
||||||
|
method: 'GET',
|
||||||
|
url: 'http://www.omdbapi.com/?t=star+wars&y=&plot=short&r=json&apikey=bea723f3'
|
||||||
|
}).then(
|
||||||
|
function(response) {
|
||||||
|
//success callback
|
||||||
|
console.log(response);
|
||||||
|
console.log(this); //this no long refers to the controller, instead it's the window object
|
||||||
|
},
|
||||||
|
function(response) {
|
||||||
|
//failure callback
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
- To get around this, save `this` in a variable before you make the call
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
this.find = function(){ //this function will get called somewhere in the DOM
|
||||||
|
const controller = this;
|
||||||
|
$http({ //once it's called, make an AJAX call to OMDB
|
||||||
|
method: 'GET',
|
||||||
|
url: 'http://www.omdbapi.com/?t=star+wars&y=&plot=short&r=json&apikey=bea723f3'
|
||||||
|
}).then(
|
||||||
|
function(response) {
|
||||||
|
//success callback
|
||||||
|
console.log(response);
|
||||||
|
console.log(this); //this no long refers to the controller, instead it's the window object
|
||||||
|
console.log(controller); //this references the controller
|
||||||
|
},
|
||||||
|
function(response) {
|
||||||
|
//failure callback
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- We can now update properties on the controller
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
$http({ //once it's called, make an AJAX call to OMDB
|
||||||
|
method: 'GET',
|
||||||
|
url: 'http://www.omdbapi.com/?t=star+wars&y=&plot=short&r=json&apikey=bea723f3'
|
||||||
|
}).then(
|
||||||
|
function(response) {
|
||||||
|
//success callback
|
||||||
|
controller.foundMovieTitle = response.data.Title; //set a property on the controller equal to the movie title found in the AJAX response
|
||||||
|
},
|
||||||
|
function(response) {
|
||||||
|
//failure callback
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
- Lastly, we can print that controller property in the DOM
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<div ng-controller="baseCtrl as base">
|
||||||
|
{{base.foundMovieTitle}}
|
||||||
|
<a ng-click="base.find()">Click me</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Alter express bodyParser to accept AJAX requests
|
||||||
|
|
||||||
|
We are now sending data via JSON, not through forms. We need to update bodyParser
|
||||||
|
|
||||||
|
file: server.js
|
||||||
|
```javascript
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
const bodyParser = require('body-parser');
|
||||||
|
|
||||||
|
app.use(express.static('public'));//static directory for js files
|
||||||
|
|
||||||
|
//change bodyParser.urlencoded({extended:false}) to bodyParser.json()
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
|
//receive POST data
|
||||||
|
app.post('/', function(req, res){
|
||||||
|
res.json(req.body); //res.json sends data back to the client in JSON format, which is easy for angular to parse
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(3000, function(){
|
||||||
|
console.log('listening');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
We can send data to the server like so:
|
||||||
|
|
||||||
|
file: public/index.html
|
||||||
|
```html
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<div ng-controller="baseCtrl as base"><!-- controller -->
|
||||||
|
<a ng-click="base.create()">Click me</a><!-- call find function -->
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: public/js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('myApp', []);
|
||||||
|
|
||||||
|
app.controller('baseCtrl', ['$http', function($http){
|
||||||
|
this.create = function(){ //this function will get called somewhere in the DOM
|
||||||
|
$http({ //once it's called, make an AJAX call to OMDB
|
||||||
|
method: 'POST',
|
||||||
|
url: '/',
|
||||||
|
data: { someProperty: 'somevalue' }
|
||||||
|
}).then(
|
||||||
|
function(response) {
|
||||||
|
//success callback
|
||||||
|
console.log(response);
|
||||||
|
},
|
||||||
|
function(response) {
|
||||||
|
//failure callback
|
||||||
|
console.log(response);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use cURL to send JSON
|
||||||
|
1. Syntax for testing Express with cURL is different, since you're not sending form data, but JSON
|
||||||
|
```
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d '{"username":"matt","password":"matty"}' http://localhost:3000/
|
||||||
|
```
|
||||||
|
1. `-H "Content-Type: application/json"`
|
||||||
|
- add a header saying that the type of file is JSON
|
||||||
|
1. `-d '{"username":"matt","password":"matty"}'`
|
||||||
|
- data must be formatted as JSON
|
||||||
|
- property names and values must be in **DOUBLE** quotes (not single)
|
||||||
@ -0,0 +1,402 @@
|
|||||||
|
# Intro to AngularJS
|
||||||
|
|
||||||
|
## Learning Objectives
|
||||||
|
- Describe what are Single Page Apps and their pros and cons
|
||||||
|
- Describe what are front-end frameworks
|
||||||
|
- Describe AngularJS and what problems it tries to solve
|
||||||
|
- Set up first Angular app
|
||||||
|
- Learn some basic angular directives
|
||||||
|
|
||||||
|
### Important
|
||||||
|
|
||||||
|
Remember in your google searches we are learning Angular 1.7 or AngularJS - the newer angular is called Angular or Angular 2.x - 6.x
|
||||||
|
|
||||||
|
## AJAX and the Start of Single Page Apps
|
||||||
|
|
||||||
|
A major advancement in web development was AJAX (Asynchronous JavaScript And XML).
|
||||||
|
|
||||||
|
What if you didn't have to reload an entire web page every time you needed to make a request? What if you only needed to reload the parts of the page that needed to be updated?
|
||||||
|
|
||||||
|
Think about google maps that are embedded in a web page. When you click on the map, move around, zoom in or out, the whole page doesn't reload, just the map.
|
||||||
|
|
||||||
|
This allows for a better user experience, since it is faster (no whole page reload), and generally smoother, (user keeps their place and can keep moving around the map).
|
||||||
|
|
||||||
|
This technology opened the doors to Single Page Apps.
|
||||||
|
|
||||||
|
## Single Page App Pros and Cons
|
||||||
|
|
||||||
|
Pros:
|
||||||
|
- Fast, resources only loaded once and then only data is transmitted back and forth
|
||||||
|
- The client's browser renders the views, rather than the server, which usually allows for a faster experience
|
||||||
|
- Separate data from views. By making the back end have end points that are just data, the data can be resused for multiple apps - desktop vs android app, vs iOS app all can use the same data/database.
|
||||||
|
|
||||||
|
Cons:
|
||||||
|
- Initial load can be slow because there could be a lot of code to download
|
||||||
|
- Building large apps gets very tricky very fast (new frameworks try to solve this over and over again)
|
||||||
|
|
||||||
|
## Front End Frameworks
|
||||||
|
You can build a single page application with vanilla javascript or jQuery.
|
||||||
|
|
||||||
|
But, much as we favored jQuery over vanilla JavaScript in unit 1 for its useful functionality and thus let us `write less and do more`, Front-End Frameworks have been developed to try to help solve common problems in developing Single Page Applications.
|
||||||
|
|
||||||
|
There are many, some of the most popular are
|
||||||
|
- Backbone
|
||||||
|
- Ember
|
||||||
|
- Meteor
|
||||||
|
- Knockout
|
||||||
|
- AngularJS (Angular 1.x)
|
||||||
|
- Angular (Angular 2.x - 5.x and beyond)
|
||||||
|
- Vue
|
||||||
|
- React
|
||||||
|
|
||||||
|
Some are popular because everyone was excited about them at one point and companies continue to use them. Others are popular because they have a lot of buzz.
|
||||||
|
|
||||||
|
As of March 2018, the landscape continues to change. There is even 'framework fatigue' where people are tired of all the options and new things being released all the time!
|
||||||
|
|
||||||
|
Front End Frameworks generally try to solve the same problems:
|
||||||
|
- Speed - all frameworks are pretty darn fast, but this is something that gets a lot of attention. Speed is an easy metric to understand for even a non-coding person. But should not be the only deciding factor
|
||||||
|
- Code organization - some stick to MVC, some combine MVC in some way, and some choose a different way to organize code. This helps with scaling the scope of the project, having many devs working with the same patterns so it is easier for people to maintain consistency and join projects
|
||||||
|
- Managing state - state has to do with the way the data is stored/represented in the database, on the web page and how the data is updated
|
||||||
|
|
||||||
|
## AngularJS
|
||||||
|
AngularJS (Angular 1.x) came out in 2009 and was the first blockbuster framework. Everyone was excited about it, everyone wanted to use it. AngularJS is built by Google. It continued to grow in popularity and features for years.
|
||||||
|
|
||||||
|
Its replacement Angular (versions 2 - 5+), came out in January 2016 and was a total rewrite of Angular and is completely incompatible with Angular 1.x. Despite this new version, many companies kept and keep using 1.x.
|
||||||
|
|
||||||
|
Angular's features include:
|
||||||
|
- Open source, free to use
|
||||||
|
- Handles the V in MVC (Models, Views, Controllers)* (not everyone agrees with this particular semantic, but it is close enough; the alternate you might see is MVVM (Model-View-View-Model))
|
||||||
|
- 2 way data binding - the data displayed on the web page is the same as the data in js, if one is updated, the other updates as well. This is really cool, but also saves you from having to dedicate code to traverse, manipulate and listen to the DOM for every change of data on your page.
|
||||||
|
- Fast start! Getting started with AngularJS is as simple as adding a link in the header, just like jQuery.
|
||||||
|
- HTML Template, write your HTML as you always would, and then add in AngularJS to provide functionality where needed.
|
||||||
|
- Directives - these mark DOM elements to interact with AngularJS, things like click events, showing/hiding elements, binding data and much more
|
||||||
|
- Services (Dependency Injection), you can use AJAX to make http requests and many other services are available that can help you build all the functionality you'd like for a Single Page Application. The fancy term 'dependency injection' - just means it is pretty easy to add services. We'll see this later in our code alongs
|
||||||
|
|
||||||
|
## Our First AngularJS App
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
In your student examples folder
|
||||||
|
- `mkdir first_app`
|
||||||
|
- `cd first_app`
|
||||||
|
- `touch index.html app.js`
|
||||||
|
- `atom .`
|
||||||
|
|
||||||
|
**index.html**
|
||||||
|
- add `html` boilerplate
|
||||||
|
- in the head tag add
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js"></script>
|
||||||
|
```
|
||||||
|
and below that link our `app.js`
|
||||||
|
```html
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Module
|
||||||
|
We will only have one module in our apps. Our modules can have many controllers.
|
||||||
|
|
||||||
|
To make sure our module encompasses all our html, we're going to put the `directive` in our opening `html` tag.
|
||||||
|
|
||||||
|
Remember a directive is just extra text in an html element tag that will let us do cool angular stuff. It always starts with `ng-`
|
||||||
|
|
||||||
|
This one is to say 'hey this is our angular app'
|
||||||
|
|
||||||
|
```html
|
||||||
|
<html ng-app="MyApp">
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's head on over to our `app.js` to configure our app.
|
||||||
|
|
||||||
|
**app.js**
|
||||||
|
```js
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
```
|
||||||
|
We're saying let's make a variable `app` that is equal to an angular.module, which is a function that takes two arguments.
|
||||||
|
|
||||||
|
The first argument **MUST** match what we named our app in our html, in this case, we named it `MyApp`, we could have named it `KarolinApp` or whatever we want, but it must match in the html and the `app.js` file.
|
||||||
|
|
||||||
|
The second argument is an empty array. Right now our app is going to be really simple and we'll just go over some fundamental AngularJS. We'll be filling in that array soon enough.
|
||||||
|
|
||||||
|
### Controller
|
||||||
|
Controllers control a certain area of the page. A controller can control it's own HTML element and the children inside of it. By default, it doesn't know anything about sibling or parent elements.
|
||||||
|
|
||||||
|
Let's write our first controller inside our
|
||||||
|
|
||||||
|
**app.js**
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.controller('MainController', function(){
|
||||||
|
this.hello = 'oh hai!';
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**GOTCHA** - we have to use the ES5 `function(){}` as our second argument. If we use an arrow function, it won't work as expected.
|
||||||
|
|
||||||
|
Super! Now let's bind our controller to our html
|
||||||
|
|
||||||
|
We'll just have one controller, so let's bind it to the body.
|
||||||
|
|
||||||
|
`MainController` is a lot of typing. We can give it an alias `ctrl` so we can type less
|
||||||
|
|
||||||
|
**index.html**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<body ng-controller = "MainController as ctrl">
|
||||||
|
```
|
||||||
|
Last step, let's get our data stored in `this.hello` to render in our HTML
|
||||||
|
|
||||||
|
Our data will match in our HTML and in our `app.js` and update whenever one is changed.
|
||||||
|
|
||||||
|
However, we access our data a little differently depending if we are in our html or js file.
|
||||||
|
|
||||||
|
When in our html file, we access the data by starting with `ctrl` which is the alias we set for `MainController`.
|
||||||
|
|
||||||
|
When in our js file, we acccess the data by starting with `this`
|
||||||
|
|
||||||
|
so in this case our data is the text string `oh hai!`
|
||||||
|
|
||||||
|
To access it in the js it is
|
||||||
|
```js
|
||||||
|
this.hello = 'oh hai!';
|
||||||
|
```
|
||||||
|
|
||||||
|
and to access it in the html:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<body ng-controller = "MainController as ctrl">
|
||||||
|
<h1>{{ ctrl.hello }}</h1>
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's test it out and see if our data renders!
|
||||||
|
|
||||||
|
All our `index.html`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="MyApp">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
<title>My First App</title>
|
||||||
|
</head>
|
||||||
|
<body ng-controller='MainController as ctrl'>
|
||||||
|
<h1>{{ ctrl.hello }}</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
All our `app.js`
|
||||||
|
|
||||||
|
```js
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('MainController', function(){
|
||||||
|
this.hello = 'oh hai!';
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Directives
|
||||||
|
|
||||||
|
We'll go over a few directives beyond what we needed to set up our app
|
||||||
|
- `ng-repeat`
|
||||||
|
- `ng-src`
|
||||||
|
- `ng-if`
|
||||||
|
- `ng-show`/ `ng-hide`
|
||||||
|
- `ng-click`
|
||||||
|
|
||||||
|
Let's make a page for our contacts.
|
||||||
|
|
||||||
|
First let's add a little css
|
||||||
|
```html
|
||||||
|
<style>
|
||||||
|
img {
|
||||||
|
max-width: 100px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
dl {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
```
|
||||||
|
|
||||||
|
### ng-repeat
|
||||||
|
Here is some data of our contacts. Let's copy paste it into our `app.js` INSIDE our `app.controller` function
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.contacts = [
|
||||||
|
{
|
||||||
|
name: 'Jenny',
|
||||||
|
phone: 8675309,
|
||||||
|
state: 'California',
|
||||||
|
image: 'http://old.unsquare.com/dance/wp-content/uploads/2008/11/jennylewis01.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Claire',
|
||||||
|
phone: 6060842,
|
||||||
|
state: 'DC',
|
||||||
|
image: 'http://www1.pictures.zimbio.com/mp/yOE-jyqpuoql.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Morris',
|
||||||
|
phone: 7779311,
|
||||||
|
state: 'Minnesota',
|
||||||
|
image: 'https://s1.ticketm.net/tm/en-us/dbimages/111807a.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Alicia',
|
||||||
|
phone: 4894608,
|
||||||
|
state: 'New York',
|
||||||
|
image: 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2a/AliciaKeys2013.jpg/220px-AliciaKeys2013.jpg'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Etta',
|
||||||
|
phone: '842-3089',
|
||||||
|
state: 'California',
|
||||||
|
image: 'http://images5.fanpop.com/image/photos/30600000/SWEET-ETTA-JAMES-etta-james-30694011-584-519.gif'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's make cards for each of our contacts. First let's just see if we can get their names to display
|
||||||
|
|
||||||
|
**Index.html**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<body ng-controller='MainController as ctrl'>
|
||||||
|
<h1>{{ ctrl.hello }}</h1>
|
||||||
|
<div ng-repeat="contact in ctrl.contacts">
|
||||||
|
<dt>{{ contact.name }}<dt>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Expected Appearance:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### ng-src
|
||||||
|
Let's build out some more of our contact list.
|
||||||
|
We'll use another directive `ng-src` to get our images to show up correctly
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div ng-repeat="contact in ctrl.contacts">
|
||||||
|
<img ng-src="{{ contact.image }}" alt="an image of {{ contact.name }}">
|
||||||
|
<dt>{{ contact.name }}<dt>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
Expected Apperance:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Let's add some more
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div ng-repeat="contact in ctrl.contacts">
|
||||||
|
<img ng-src="{{ contact.image }}" alt="an image of {{ contact.name }}">
|
||||||
|
<dl>
|
||||||
|
<dt> {{ contact.name }} </dt>
|
||||||
|
<dd> {{ contact.state }} </dd>
|
||||||
|
<dd> {{ contact.phone }} </dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Cool! We can see that `ng-repeat` let's us loop over our array of objects and make a new element for each contact
|
||||||
|
|
||||||
|
### ng-if
|
||||||
|
We may not want everything to appear. We can control what appears on the page using `ng-if`
|
||||||
|
|
||||||
|
in **app.js**
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.showImages = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
in **index.html**
|
||||||
|
```html
|
||||||
|
<img ng-src="{{ contact.image }}" alt="an image of {{ contact.name }}" ng-if="ctrl.showImages">
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, all of our images should disappear.
|
||||||
|
|
||||||
|
Expected Appearance:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
We can look into our Chrome console elements tab for more info
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**app.js**
|
||||||
|
|
||||||
|
Let's change that false to true
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.showImages = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
Our images should reappear and in chrome console we should now see our img element. Before we just had a comment about the `ng-if` and our element was gone. Now it should be back
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## ng-show / ng-hide
|
||||||
|
|
||||||
|
In **app.js**
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.showInfo = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
In **index.html**
|
||||||
|
```html
|
||||||
|
<dl>
|
||||||
|
<dt> {{ contact.name }} </dt>
|
||||||
|
<dd ng-show="ctrl.showInfo"> {{ contact.state}} </dd>
|
||||||
|
<dd ng-show="ctrl.showInfo"> {{ contact.phone}} </dd>
|
||||||
|
</dl>
|
||||||
|
```
|
||||||
|
In contrast to `ng-if` `ng-show` changes to `ng-hide` and the element is still on the page
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## ng-click
|
||||||
|
|
||||||
|
We need a way to be able to interact with our page.
|
||||||
|
|
||||||
|
Let's write a function that will toggle our `this.showInfo` value
|
||||||
|
|
||||||
|
**app.js**
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.toggleInfo = () =>{
|
||||||
|
this.showInfo = !this.showInfo;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now let's add a click event to a button that will call this function
|
||||||
|
|
||||||
|
|
||||||
|
```html
|
||||||
|
<body ng-controller='MainController as ctrl'>
|
||||||
|
<h1>{{ ctrl.hello }}</h1>
|
||||||
|
<button ng-click="ctrl.toggleInfo()"> Toggle Info </button>
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, as we update the value of `this.showInfo` our page changes to reflect that.
|
||||||
|
|
||||||
|
|
||||||
|
Resources
|
||||||
|
[SPA vs MPA](https://medium.com/@NeotericEU/single-page-application-vs-multiple-page-application-2591588efe58)
|
||||||
|
|
||||||
|
[AngularJS](https://www.tutorialspoint.com/angularjs/angularjs_overview.htm)
|
||||||
|
[5 Awesome AngularJS Features](https://code.tutsplus.com/tutorials/5-awesome-angularjs-features--net-25651)
|
||||||
|
|
||||||
|
[How it feels to learn JavaScript in 2016](https://hackernoon.com/how-it-feels-to-learn-javascript-in-2016-d3a717dd577f)
|
||||||
|
|
||||||
|
[How it feels to learn JavaScript in 2017](https://medium.com/front-end-hacking/how-it-feels-to-learn-javascript-in-2017-a934b801fbe)
|
||||||
@ -0,0 +1,421 @@
|
|||||||
|
# Intro to AJAX and AngularJS HTTP Service
|
||||||
|
|
||||||
|
## Learning Objectives
|
||||||
|
- Describe what is AJAX
|
||||||
|
- Describe what AJAX is used for
|
||||||
|
- Describe what are 3rd Party APIs and How they are used
|
||||||
|
- Describe what are API keys and how to use them
|
||||||
|
- Review Query Parameters and how they can be used in 3rd Party API requests
|
||||||
|
- Set up $http service for an Angular App
|
||||||
|
- Learn how to use $http to make requests
|
||||||
|
- Learn how to incorporate data from $http requests into a SPA using AngularJS
|
||||||
|
|
||||||
|
## AJAX
|
||||||
|
|
||||||
|
AJAX stands for Asynchronous JavaScript an XML.
|
||||||
|
|
||||||
|
XML was once a popular way to store and send data over the internet and it is still used. However, JSON has become the predominant way to send data over the internet.
|
||||||
|
|
||||||
|
When we will use AJAX, we will be sending and receiving JSON.
|
||||||
|
|
||||||
|
AJAX allows us to only reload portions of a web page. Thinking of an embedded google map, you can click around it and change the view/data, without having to reload the page every single time.
|
||||||
|
|
||||||
|
## Third Party APIs
|
||||||
|
|
||||||
|
Many web sites have their own data, but they can pull in other data. For example, many news sites have a weather widget. This widget gets its data from a weather resource.
|
||||||
|
|
||||||
|
There are many APIs that can be used by individuals and companies. Some are totally free, some are available for a small fee, and some are really expensive.
|
||||||
|
|
||||||
|
There are APIs for
|
||||||
|
- Weather
|
||||||
|
- Stocks
|
||||||
|
- Beer
|
||||||
|
- Dictionaries
|
||||||
|
- Books
|
||||||
|
- Sports
|
||||||
|
- Art
|
||||||
|
- Games
|
||||||
|
- Movies
|
||||||
|
|
||||||
|
[Here is one list of APIs](https://github.com/toddmotto/public-apis)
|
||||||
|
|
||||||
|
## API Keys
|
||||||
|
|
||||||
|
Many APIs are restricted. Maintaining data on a server can get expensive and the data on a lot of these sites is valuable.
|
||||||
|
|
||||||
|
The two main ways individuals/companies can get access to APIs is through API keys - a special set of characters that is purchased through the website. Every time you make a request, the key must be used, this lets the API keep track of how many requests you make and limit/charge you accordingly.
|
||||||
|
|
||||||
|
The other way is OAuth. OAuth is a tangent to what we'll talk about today, but if you want to learn more, here is a [good start](https://stackoverflow.com/questions/4201431/what-exactly-is-oauth-open-authorization).
|
||||||
|
|
||||||
|
Typically, API keys go in as query parameters. Query parameters go at the end of a URL. They start with a `?` and then have key value pairs. Many key-value pairs can be used and each key-value pair can be used by separating them with an equal sign.
|
||||||
|
|
||||||
|
Here is an example of a request to OMDB (open movie data base), for a movie with the title `Eraserhead` and the `apikey` (this is a fake key).
|
||||||
|
|
||||||
|
```
|
||||||
|
http://www.omdbapi.com/?t=Eraserhead&apikey=x9903Ddc
|
||||||
|
```
|
||||||
|
|
||||||
|
A response in JSON will be sent and could be seen in the browser
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Mini Movie App
|
||||||
|
|
||||||
|
We're going to build an AngularJS single page app that has a text input, a button and when a user inputs a movie, they will get a set of movies that match from OMDB.
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
In student_examples
|
||||||
|
- `mkdir movies`
|
||||||
|
- `cd movies`
|
||||||
|
- `touch app.js index.html`
|
||||||
|
- `atom .`
|
||||||
|
|
||||||
|
**Index.html**
|
||||||
|
- html boilerplate
|
||||||
|
- link angular
|
||||||
|
```
|
||||||
|
https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js
|
||||||
|
```
|
||||||
|
- link `app.js` below angular
|
||||||
|
- set `<html ng-app="MovieApp">`
|
||||||
|
- set `<body ng-controller="MainController as ctrl">`
|
||||||
|
- Inside the body include {{ 2+ 2 }} this should evaluate and display the number 4 when we have everything set up correctly
|
||||||
|
|
||||||
|
**app.js**
|
||||||
|
|
||||||
|
- set a variable app to the angular module. Remember the `ng-app` name in the html must match the string
|
||||||
|
|
||||||
|
```js
|
||||||
|
const app = angular.module('MoviesApp', []);
|
||||||
|
```
|
||||||
|
|
||||||
|
We're going to use the $http service, so we need to configure it.
|
||||||
|
|
||||||
|
In our `app.controller`, our first argument will still be `MainController`
|
||||||
|
|
||||||
|
Our second argument will be an array.
|
||||||
|
|
||||||
|
The array will have two items. The first is a string `$http`, and the second is an ES5 function with `$http` passed in as an argument.
|
||||||
|
|
||||||
|
The set of brackets is going to look a bit odd at first, but we'll get used to it.
|
||||||
|
|
||||||
|
We'll be coding all our angular functionality inside the curly braces.
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.controller('MainController', ['$http', function($http) {
|
||||||
|
// code goes here
|
||||||
|
}]); // closes app.controller
|
||||||
|
```
|
||||||
|
|
||||||
|
If we did it right, we should see the number 4 evaluate in the browser
|
||||||
|
|
||||||
|
Let's include the title as data from our angular app. That will let us quickly know if we have an error with our basic code.
|
||||||
|
|
||||||
|
**app.js**
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.controller('MainController', ['$http', function($http) {
|
||||||
|
this.appName = "Gerard Von Schtieffel's MüvieHaüs"
|
||||||
|
}]); // closes app.controller
|
||||||
|
```
|
||||||
|
|
||||||
|
**index.html**
|
||||||
|
```html
|
||||||
|
<body ng-controller="MainController as ctrl">
|
||||||
|
<h1>Welcome to {{ctrl.appName}}</h1>
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using $http
|
||||||
|
|
||||||
|
We're going to be making requests to OMDB using `$http`. We'll be viewing those results in Chrome's Console. Once we build out the functionality, we'll start incorporating our data into our web page.
|
||||||
|
|
||||||
|
Go to [request an api key from OMDB](http://www.omdbapi.com/apikey.aspx)
|
||||||
|
|
||||||
|
Fill out the form, you should get an email asking you to validate your key within a few minutes. Hold on to this key, we'll be using it soon enough.
|
||||||
|
|
||||||
|
`$http` is a powerful AngularJS function. jQuery has a similar function called `$.ajax`. Vanilla JS also has its own version called 'fetch'. All follow a very similar pattern. Since we are already using AngularJS we'll use Angular's.
|
||||||
|
|
||||||
|
An http request requires:
|
||||||
|
- headers ($http handles most of this for us)
|
||||||
|
- a url to send the request
|
||||||
|
- a method (get, post, put, delete...)
|
||||||
|
- a way to send data if it is a post or put request
|
||||||
|
- a way to wait for the response before doing something with the incoming data
|
||||||
|
- a way to do something with the incoming data
|
||||||
|
- a way to handle errors
|
||||||
|
|
||||||
|
Let's build our first $http request.
|
||||||
|
|
||||||
|
`$http` is a function. it takes one argument which **MUST** be an object. We'll code this out together after we put together our query string
|
||||||
|
|
||||||
|
The object requires a minimum of two key value pairs
|
||||||
|
- `method` - a string: `GET`, `POST`, `PUT`, `DELETE`
|
||||||
|
- `url` - a string of where to send the request
|
||||||
|
|
||||||
|
- if the request is sending data (ie input from input fields), a third key value pair is required
|
||||||
|
- `data`: an object of key value pairs
|
||||||
|
Since we are just sending GET requests, we won't need `data` for now
|
||||||
|
|
||||||
|
**app.js**
|
||||||
|
|
||||||
|
Let's build our url
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.baseURL = 'http://www.omdbapi.com/?';
|
||||||
|
this.apikey = 'apikey=' + 'your api key here';
|
||||||
|
this.query = 't=';
|
||||||
|
this.movieTitle = 'Eraserhead';
|
||||||
|
this.searchURL = this.baseURL + this.apikey + '&' + this.query + this.movieTitle;
|
||||||
|
|
||||||
|
console.log(this.searchURL);
|
||||||
|
```
|
||||||
|
our searchURL should look like (the api key is fake, it should be the one you got via email)
|
||||||
|
|
||||||
|
```js
|
||||||
|
http://www.omdbapi.com/?apikey=9999999&t=Eraserhead
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: including your API key in the `app.js` and then pushing it up to github makes your API key findable. OMDB keys are not that valuable, so it shouldn't be a worry.
|
||||||
|
|
||||||
|
However, there are services that cost thousands of dollars a month. People write bots to look for exposed API keys to steal them. We don't have time to cover hiding API keys today. But keep it in mind for your projects.
|
||||||
|
|
||||||
|
```js
|
||||||
|
$http()
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
$http({})
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: this.searchURL
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
This will send our request, but now we have to handle the response. The $http function returns a promise. A promise is an object that handles asynchronous operations.
|
||||||
|
|
||||||
|
Remember, JavaScript just runs through the code top to bottom. It doesn't wait for one thing to finish before moving on to the next thing.
|
||||||
|
|
||||||
|
One way we've been handling waiting is by using callbacks. Callbacks can get unwieldy. Promises can be a nicer way to write our code. Often you'll be working with promises that were already created for you from a library or framework (like `$http`)
|
||||||
|
|
||||||
|
A promise is a way of writing code that says 'hey, wait for me, and I _promise_ I'll send you a response soon. _Then_, you can do what you need to do'
|
||||||
|
|
||||||
|
A promise has three states
|
||||||
|
- Pending
|
||||||
|
- Fulfilled (completed successfully)
|
||||||
|
- Rejected (it failed)
|
||||||
|
|
||||||
|
After calling the initial function (in our case `$http()`), all we need to do is chain another function `.then()`
|
||||||
|
|
||||||
|
`.then()` takes two callback functions.
|
||||||
|
|
||||||
|
The first one is `onFulfilled` this one is executed if the promise is fulfilled. This function can do whatever we want, for now, we'll just console.log the response.
|
||||||
|
|
||||||
|
The second one is `onRejected` this one is executed instead of the first one if the promise has failed in some way.
|
||||||
|
|
||||||
|
```js
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: this.searchURL
|
||||||
|
}).then( response => {
|
||||||
|
console.log(response);
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected Appearance:
|
||||||
|

|
||||||
|
|
||||||
|
## Extracting the data from our response
|
||||||
|
|
||||||
|
Our response has a lot of useful stuff in it, status, headers, config... useful stuff that our users do not care about.
|
||||||
|
|
||||||
|
Our users care about the data about the movie. When we look at the object we get back in the console, we can see our data of interest is inside `data` so let's grab it.
|
||||||
|
|
||||||
|
```js
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: this.searchURL
|
||||||
|
}).then( response => {
|
||||||
|
console.log(response.data);
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected Appearance:
|
||||||
|

|
||||||
|
|
||||||
|
## Error handling
|
||||||
|
|
||||||
|
When things work, it's awesome. When things fail, having helpful errors can help us track down what is wrong.
|
||||||
|
|
||||||
|
Our `.then()` function takes a second argument, a callback to handle errors.
|
||||||
|
|
||||||
|
Also, promises have a `.catch()` method that help us catch any other errors. Let's code it out.
|
||||||
|
|
||||||
|
```js
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: this.searchURL
|
||||||
|
}).then( response => {
|
||||||
|
console.log(response.data);
|
||||||
|
}, error => {
|
||||||
|
console.error( error );
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: this.searchURL
|
||||||
|
}).then( response => {
|
||||||
|
console.log(response.data);
|
||||||
|
}, error => {
|
||||||
|
console.error( error );
|
||||||
|
}).catch( err => console.error('Catch: ' , err ));
|
||||||
|
```
|
||||||
|
## putting $http in a function
|
||||||
|
|
||||||
|
Right now, as soon as we load the page, we send this get request. We need a way to control when this function is executed.
|
||||||
|
|
||||||
|
Let's wrap it in a function
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.getMovies = () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
copy paste our $http function into `this.getMovies`
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.getMovies = () => {
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: this.searchURL
|
||||||
|
}).then( response => {
|
||||||
|
console.log(response.data);
|
||||||
|
}, error => {
|
||||||
|
console.error( error );
|
||||||
|
}).catch( err => console.error('Catch: ' , err ));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Triggering our function from our web page
|
||||||
|
|
||||||
|
Let's create a form and on submit, it will send our request.
|
||||||
|
|
||||||
|
We'll use a new directive `ng-submit` so that when the submit button is pushed it will trigger whatever function is assigned to it.
|
||||||
|
|
||||||
|
**index.html**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form ng-submit="ctrl.getMovies()">
|
||||||
|
<button type="submit">SUBMIT</button>
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's test it, don't forget to push the button.
|
||||||
|
|
||||||
|
Expected Appearance:
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### Making our data appear on the page
|
||||||
|
|
||||||
|
We're going to need a variable that is visible to both our app.js and index.js that we can store our movie data.
|
||||||
|
|
||||||
|
At first we'll just be grabbing one movie, but we'll hope to be able to grab an array. So we'll name our variable movies
|
||||||
|
|
||||||
|
**app.js**
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.movies = [];
|
||||||
|
|
||||||
|
this.getMovies = () => {
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: this.searchURL
|
||||||
|
}).then( response => {
|
||||||
|
this.movies = [response.data ];
|
||||||
|
}, error => {
|
||||||
|
console.error( error );
|
||||||
|
}).catch( err => console.error('Catch: ' , err ));
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
**index.html**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div ng-repeat="movie in ctrl.movies">
|
||||||
|
<h2>{{ movie.Title }} {{ movie.Year }}</h2>
|
||||||
|
<img ng-src='{{ movie.Poster }}' />
|
||||||
|
<h3> Starring: {{ movie.Actors }} </h3>
|
||||||
|
<h4> Plot: {{ movie.Plot }} </h4>
|
||||||
|
<h5> Genre: {{ movie.Genre }} </h5>
|
||||||
|
<h6> Rated: {{ movie.Rated }} </h6?
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
## Making Specific Requests
|
||||||
|
|
||||||
|
Right now our movie app is hard coded to always return info about Eraserhead.
|
||||||
|
|
||||||
|
It would be great to look for any movie.
|
||||||
|
|
||||||
|
Let's make an input.
|
||||||
|
|
||||||
|
We'll use the directive `ng-model` that will grab whatever we put in our input field. The value of `ng-model` is they key, the property will be whatever is in the input field.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form ng-submit="ctrl.getMovies()">
|
||||||
|
<input type="text" placeholder="movie title" ng-model="ctrl.movieTitle" />
|
||||||
|
<button type="submit">SUBMIT</button>
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's adjust our **app.js**
|
||||||
|
|
||||||
|
- change this.movieTitle to an empty string
|
||||||
|
- remove `this.movieTitle` from `this.searchURL`
|
||||||
|
- add `this.movieTitle` to the `url` inside the `$http` function
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.movies =[];
|
||||||
|
this.movieTitle ='';
|
||||||
|
this.baseURL = 'http://www.omdbapi.com/?';
|
||||||
|
this.apikey = 'apikey=' + '9999999';
|
||||||
|
this.query = 't='
|
||||||
|
this.searchURL = this.baseURL + this.apikey + '&' + this.query;
|
||||||
|
```
|
||||||
|
|
||||||
|
Update our url inside our function
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.getMovies = () => {
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: this.searchURL + this.movieTitle;
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can search for whatever movie we want.
|
||||||
|
|
||||||
|
## Lab
|
||||||
|
|
||||||
|
Our app could still use some upgrades.
|
||||||
|
|
||||||
|
Right now it only shows one movie.
|
||||||
|
|
||||||
|
Research the OMDB docs on how to get a list (hint research `s=` we've been using `t=`)
|
||||||
|
|
||||||
|
Then, only display the movie titles. Allow the user to click on a movie and see the movie's details.
|
||||||
@ -0,0 +1,168 @@
|
|||||||
|
# Create an API
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Define API
|
||||||
|
1. Initialize Directory
|
||||||
|
1. Set Up Express Server
|
||||||
|
1. Create Todo Controller
|
||||||
|
1. Initialize Mongoose
|
||||||
|
1. Create Todo Model
|
||||||
|
1. Create Create Route
|
||||||
|
1. Create Index Route
|
||||||
|
1. Create Delete Route
|
||||||
|
1. Create Update Route
|
||||||
|
|
||||||
|
## Define API
|
||||||
|
|
||||||
|
- An API stands for Application Program Interface
|
||||||
|
- It is a set of routines, protocols, and tools for building software applications
|
||||||
|
- It specifies how software components should interact
|
||||||
|
- Essentially it's documentation, but in the industry, it's come to mean a program or some existing software that you use to build your own app
|
||||||
|
|
||||||
|
## Initialize Directory
|
||||||
|
|
||||||
|
1. `npm init`
|
||||||
|
1. set entry point as server.js
|
||||||
|
1. `touch server.js`
|
||||||
|
1. `npm install express`
|
||||||
|
|
||||||
|
## Set Up Express Server
|
||||||
|
|
||||||
|
server.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.listen(3000, ()=>{
|
||||||
|
console.log('listening...');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create Todo Controller
|
||||||
|
|
||||||
|
1. `mkdir controllers`
|
||||||
|
1. `touch controllers/todos.js`
|
||||||
|
|
||||||
|
controllers/todos.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/', (req, res)=>{
|
||||||
|
res.send('index');
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
```
|
||||||
|
|
||||||
|
server.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const todosController = require('./controllers/todos.js');
|
||||||
|
app.use('/todos', todosController);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Initialize Mongoose
|
||||||
|
|
||||||
|
1. `npm install mongoose`
|
||||||
|
|
||||||
|
server.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
//...farther down the page
|
||||||
|
mongoose.connect('mongodb://localhost:27017/meancrud', { useNewUrlParser: true, useUnifiedTopology: true });
|
||||||
|
mongoose.connection.once('open', ()=>{
|
||||||
|
console.log('connected to mongod...');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Open terminal tabs for `mongod` and `mongo`
|
||||||
|
|
||||||
|
## Create Todo Model
|
||||||
|
|
||||||
|
1. `mkdir models`
|
||||||
|
1. `touch models/todos.js`
|
||||||
|
|
||||||
|
models/todos.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const todoSchema = new mongoose.Schema({
|
||||||
|
description: String,
|
||||||
|
complete: Boolean
|
||||||
|
});
|
||||||
|
|
||||||
|
const Todos = mongoose.model('Todo', todoSchema);
|
||||||
|
|
||||||
|
module.exports = Todos;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create Create Route
|
||||||
|
|
||||||
|
1. We need to tell Express to expect JSON data in the body from AJAX, so we'll use `express.json()`
|
||||||
|
1. We'll also need to tell the client that the data coming back is JSON, not HTML, so we'll do `res.json()`
|
||||||
|
|
||||||
|
server.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.use(express.json()); //use .json(), not .urlencoded()
|
||||||
|
```
|
||||||
|
|
||||||
|
controllers/todos.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const Todos = require('../models/todos.js');
|
||||||
|
//...farther down the page
|
||||||
|
router.post('/', (req, res)=>{
|
||||||
|
Todos.create(req.body, (err, createdTodo)=>{
|
||||||
|
res.json(createdTodo); //.json() will send proper headers in response so client knows it's json coming back
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
test: `curl -X POST -H "Content-Type: application/json" -d '{"description":"weee","complete":true}' http://localhost:3000/todos`
|
||||||
|
|
||||||
|
## Create Index Route
|
||||||
|
|
||||||
|
controllers/todos.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
router.get('/', (req, res)=>{
|
||||||
|
Todos.find({}, (err, foundTodos)=>{
|
||||||
|
res.json(foundTodos);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
test: `curl http://localhost:3000/todos`
|
||||||
|
|
||||||
|
## Create Delete Route
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
router.delete('/:id', (req, res)=>{
|
||||||
|
Todos.findByIdAndRemove(req.params.id, (err, deletedTodo)=>{
|
||||||
|
res.json(deletedTodo);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
test: `curl -X DELETE http://localhost:3000/todos/58f79d490708714536c02474`
|
||||||
|
|
||||||
|
## Create Update Route
|
||||||
|
|
||||||
|
controllers/todos.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
router.put('/:id', (req, res)=>{
|
||||||
|
Todos.findByIdAndUpdate(req.params.id, req.body, {new:true}, (err, updatedTodo)=>{
|
||||||
|
res.json(updatedTodo);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
test: `curl -X PUT -H "Content-Type: application/json" -d '{"description":"I updated this","complete":true}' http://localhost:3000/todos/58f7a4fd26b1a345e9281cb8`
|
||||||
@ -0,0 +1,161 @@
|
|||||||
|
# MEAN Stack
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Describe the elements of the MEAN stack
|
||||||
|
1. Set up static files
|
||||||
|
1. Set up Angular
|
||||||
|
1. Create App/Controller
|
||||||
|
1. Create new todo form
|
||||||
|
1. Hook form up to controller properties
|
||||||
|
1. Make form make AJAX request on submit
|
||||||
|
|
||||||
|
## Describe the elements of the MEAN stack
|
||||||
|
|
||||||
|
MEAN stack is just a collection of tech that work well together
|
||||||
|
|
||||||
|
- Mongo
|
||||||
|
- Express
|
||||||
|
- Angular
|
||||||
|
- Node.js
|
||||||
|
|
||||||
|
## Set up static files
|
||||||
|
|
||||||
|
1. `mkdir public`
|
||||||
|
1. `touch public/index.html`
|
||||||
|
1. `mkdir public/js`
|
||||||
|
1. `touch public/js/app.js`
|
||||||
|
|
||||||
|
server.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.use(express.static('public'));
|
||||||
|
```
|
||||||
|
|
||||||
|
public/index.html:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Set up Angular
|
||||||
|
|
||||||
|
1. Include Angular
|
||||||
|
1. Set up app
|
||||||
|
1. Test {{}}
|
||||||
|
|
||||||
|
public/index.html:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{2+2}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create App/Controller
|
||||||
|
|
||||||
|
public/js/app.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('MyController', function(){
|
||||||
|
this.foo = 'bar';
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
1. ng-app
|
||||||
|
1. script tag for /js/app.js
|
||||||
|
1. ng-controller
|
||||||
|
1. {{ctrl.foo}}
|
||||||
|
|
||||||
|
public/index.html:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="MyApp">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
|
||||||
|
<script src="/js/app.js" charset="utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-controller="MyController as ctrl">
|
||||||
|
{{ctrl.foo}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create new todo form
|
||||||
|
|
||||||
|
public/index.html:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form>
|
||||||
|
<input type="text" placeholder="description"/>
|
||||||
|
Complete: <input type="checkbox"/>
|
||||||
|
<input type="submit" value="Create New To Do Item"/>
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hook form up to controller properties
|
||||||
|
|
||||||
|
public/index.html:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form>
|
||||||
|
{{ctrl.description}} - {{ctrl.complete}}
|
||||||
|
<input type="text" ng-model="ctrl.description" placeholder="description"/>
|
||||||
|
Complete: <input type="checkbox" ng-model="ctrl.complete"/>
|
||||||
|
<input type="submit" value="Create New Todo Item"/>
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Make form make AJAX request on submit
|
||||||
|
|
||||||
|
public/js/app.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.controller('MyController', ['$http', function($http){
|
||||||
|
this.description = null;
|
||||||
|
this.complete = false;
|
||||||
|
|
||||||
|
this.createTodo = function(){
|
||||||
|
$http({
|
||||||
|
method:'POST',
|
||||||
|
url: '/todos',
|
||||||
|
data: {
|
||||||
|
description: this.description,
|
||||||
|
complete: this.complete
|
||||||
|
}
|
||||||
|
}).then(function(response){
|
||||||
|
console.log(response);
|
||||||
|
}, function(){
|
||||||
|
console.log('error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
public/index.html
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form ng-submit="ctrl.createTodo()">
|
||||||
|
```
|
||||||
@ -0,0 +1,231 @@
|
|||||||
|
# MEAN Stack Part 2
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Create a function to get all todos
|
||||||
|
1. On page load, show all todos
|
||||||
|
1. After creating a new todo, refresh list of todos
|
||||||
|
1. Clicking on a todo toggles it complete/incomplete
|
||||||
|
1. Create a delete button
|
||||||
|
1. Edit the Todo
|
||||||
|
|
||||||
|
## Create a function to get all todos
|
||||||
|
|
||||||
|
public/js/app.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.getTodos = function(){
|
||||||
|
$http({
|
||||||
|
method:'GET',
|
||||||
|
url: '/todos',
|
||||||
|
}).then(function(response){
|
||||||
|
console.log(response);
|
||||||
|
}, function(){
|
||||||
|
console.log('error');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getTodos(); //call immediately once controller is instantiated to test
|
||||||
|
```
|
||||||
|
|
||||||
|
## On page load, show all todos
|
||||||
|
|
||||||
|
1. create `controller` variable set to `this`
|
||||||
|
1. set property one controller when ajax succeeds
|
||||||
|
1. call getTodos during controller instantiation
|
||||||
|
|
||||||
|
public/js/app.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.controller('MyController', ['$http', function($http){
|
||||||
|
this.description = null;
|
||||||
|
this.complete = false;
|
||||||
|
const controller = this; //create controller var
|
||||||
|
|
||||||
|
//...farther down the page
|
||||||
|
this.getTodos = function(){
|
||||||
|
$http({
|
||||||
|
method:'GET',
|
||||||
|
url: '/todos',
|
||||||
|
}).then(function(response){
|
||||||
|
controller.todos = response.data; //set value on success
|
||||||
|
}, function(){
|
||||||
|
console.log('error');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getTodos(); //call immediately once controller is instantiated
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
public/index.html:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
ng-repeat="todo in ctrl.todos"
|
||||||
|
ng-class="(todo.complete)?'complete':''">
|
||||||
|
{{todo.description}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
```
|
||||||
|
|
||||||
|
1. `mkdir public/css`
|
||||||
|
1. `touch public/css/app.css`
|
||||||
|
|
||||||
|
public/css/app.css:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.complete {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
public/index.html:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link rel="stylesheet" href="/css/app.css">
|
||||||
|
```
|
||||||
|
|
||||||
|
## After creating a new todo, refresh list of todos
|
||||||
|
|
||||||
|
public/js/app.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$http({
|
||||||
|
method:'POST',
|
||||||
|
url: '/todos',
|
||||||
|
data: {
|
||||||
|
description: this.description,
|
||||||
|
complete: this.complete
|
||||||
|
}
|
||||||
|
}).then(function(response){
|
||||||
|
controller.getTodos(); //get all todos when new element is added
|
||||||
|
}, function(){
|
||||||
|
console.log('error');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Clicking on a todo toggles it complete/incomplete
|
||||||
|
|
||||||
|
public/index.html:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
ng-class=" (todo.complete) ? 'complete' : 'incomplete' "
|
||||||
|
ng-repeat="todo in ctrl.todos">
|
||||||
|
<span ng-click="ctrl.toggleTodoComplete(todo);">{{todo.description}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
```
|
||||||
|
|
||||||
|
public/js/app.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.toggleTodoComplete = function(todo){
|
||||||
|
let newCompleteValue;
|
||||||
|
if(todo.complete === true){
|
||||||
|
newCompleteValue = false;
|
||||||
|
} else {
|
||||||
|
newCompleteValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$http({
|
||||||
|
method:'PUT',
|
||||||
|
url: '/todos/' + todo._id,
|
||||||
|
data: {
|
||||||
|
description: todo.description,
|
||||||
|
complete: newCompleteValue
|
||||||
|
}
|
||||||
|
}).then(function(response){
|
||||||
|
controller.getTodos();
|
||||||
|
}, function(){
|
||||||
|
console.log('error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create a delete button
|
||||||
|
|
||||||
|
In the `<li>` for each todo:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<button ng-click="ctrl.deleteTodo(todo)">Delete This Todo</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.deleteTodo = function(todo){
|
||||||
|
$http({
|
||||||
|
method:'DELETE',
|
||||||
|
url: '/todos/' + todo._id
|
||||||
|
}).then(
|
||||||
|
function(response){
|
||||||
|
controller.getTodos();
|
||||||
|
},
|
||||||
|
function(error){
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Edit the Todo
|
||||||
|
|
||||||
|
In the `<li>` for each todo:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form ng-submit="ctrl.editTodo(todo);">
|
||||||
|
<input type="text" ng-model="ctrl.updatedDescription" placeholder="description" />
|
||||||
|
<input type="submit" value="Update Description">
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.editTodo = function(todo){
|
||||||
|
$http({
|
||||||
|
method:'PUT',
|
||||||
|
url: '/todos/' + todo._id,
|
||||||
|
data: {
|
||||||
|
description: this.updatedDescription,
|
||||||
|
complete: todo.complete
|
||||||
|
}
|
||||||
|
}).then(
|
||||||
|
function(response){
|
||||||
|
controller.getTodos();
|
||||||
|
},
|
||||||
|
function(error){
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To hide extra edit fields:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.indexOfEditFormToShow = null;
|
||||||
|
```
|
||||||
|
|
||||||
|
```html
|
||||||
|
<a
|
||||||
|
ng-if="$index !== ctrl.indexOfEditFormToShow"
|
||||||
|
href="#"
|
||||||
|
ng-click="ctrl.indexOfEditFormToShow = $index">
|
||||||
|
Edit This Todo
|
||||||
|
</a>
|
||||||
|
<form ng-if="$index === ctrl.indexOfEditFormToShow" ng-submit="ctrl.editTodo(todo);">
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
controller.indexOfEditFormToShow = null; //add this inside success callback to editTodo $http request
|
||||||
|
```
|
||||||
|
|
||||||
|
Fix styling of line-through:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<li ng-repeat="todo in ctrl.todos">
|
||||||
|
<span
|
||||||
|
ng-class=" (todo.complete) ? 'complete' : '' "
|
||||||
|
ng-click="ctrl.toggleTodoComplete(todo)">{{todo.description}}</span>
|
||||||
|
```
|
||||||
@ -0,0 +1 @@
|
|||||||
|
Start with [install and test API](./readmes/install_test_api.md)
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
# Lab - Afternoon
|
||||||
|
|
||||||
|
Keep Building the Holiday App
|
||||||
|
|
||||||
|
## Warm Up
|
||||||
|
|
||||||
|
|
||||||
|
- Add a `td` element to the table that shows the number of likes
|
||||||
|
- Add a `td` element to the table that has an image of balloons, when you click it, the number of likes go up
|
||||||
|
- In the assets folder is an image `two-balloon-icons-68911.png`
|
||||||
|
|
||||||
|
Hints:
|
||||||
|
- Create a new function with an `$http` call that specifically updates just the likes
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## More Challenging
|
||||||
|
|
||||||
|
|
||||||
|
- Add a `td` element to the table that has an image of a pencil, when you click it, a modal will appear with a form to edit
|
||||||
|
- In the assets folder is an image `simpleiconDOTcom-pen-15-64x64.png`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Hints**
|
||||||
|
- Starter Form - this should give you a nice layout that plays well with the CSS already in place. Note when the class `edit` is present it shows the modal, when the class `edit` is removed the modal disappears.
|
||||||
|
|
||||||
|
- There is also another css class `.dim` which when added to the `body`, will dim the apperance of the `body`, which you can also toggle with angular `ng-class` - if you go this route it is recommended you set a variable to true/false and create a function that allows you to toggle this value.
|
||||||
|
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="modal edit">
|
||||||
|
<form>
|
||||||
|
<div class="row">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input type="text" placeholder="" id="name">
|
||||||
|
<label for="celebrated">Celebrated</label>
|
||||||
|
<input type="text" placeholder="" id="celebrated">
|
||||||
|
<label for="likes">Likes</label>
|
||||||
|
<input type="number" placeholder="" id="likes">
|
||||||
|
<label for="description">Description</label>
|
||||||
|
<textarea class="u-full-width" placeholder="" id="description"></textarea>
|
||||||
|
<input type="submit" value="Update Holiday" class="button-primary">
|
||||||
|
<button class="button-red"> Don't Update </button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
- One way to solve it is to have, on click, the ability to grab the holiday object being clicked and store it in a global variable for reuse in other functions
|
||||||
|
|
||||||
|
- Review the create form the edit route and show one functionality we created and mix and match the functionality you need
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Ultra
|
||||||
|
- Add date to the schema. Use the JavaScript date object, mongoose Date, and moment.js to enter dates properly and also to format them that displays them in a user friendly way.
|
||||||
@ -0,0 +1,164 @@
|
|||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Completed `angular_set_up.md`
|
||||||
|
1. Create new holiday form
|
||||||
|
1. Hook form up to controller properties
|
||||||
|
1. Make form make AJAX request on submit
|
||||||
|
1. Check that the request was successful
|
||||||
|
|
||||||
|
|
||||||
|
## Upgrade our `MainController` in `app.js` to use the `$http` module
|
||||||
|
- You'll need the `$http` request module in order to interact with our express server
|
||||||
|
- We'll need to carefully upgrade our `app.Controller` to have a second argument as an array, and then pass in the value as a parameter in the callback function (still inside the array!)
|
||||||
|
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
```js
|
||||||
|
const app = angular.module('HolidaysApp', [])
|
||||||
|
|
||||||
|
app.controller('MainController', ['$http', function ($http) {
|
||||||
|
this.h5 = 'Holidays! Celebrate!'
|
||||||
|
}])
|
||||||
|
```
|
||||||
|
|
||||||
|
- Let's test it to make sure it didn't break our code. Our `Holidays! Celebrate!` message should still appear in the browser
|
||||||
|
|
||||||
|
## Create new holiday form
|
||||||
|
|
||||||
|
**In `index.html`:**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<body ng-controller="MainController as ctrl">
|
||||||
|
<div class="container">
|
||||||
|
<h5> {{ ctrl.h5 }} </h5>
|
||||||
|
<form>
|
||||||
|
<input type="text" placeholder="Holiday name" />
|
||||||
|
<input type="submit" value="Create New Holiday" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hook Form Up to Controller Properties
|
||||||
|
|
||||||
|
**Remember**
|
||||||
|
|
||||||
|
- use `ng-model` inside the `input` tags
|
||||||
|
- For now, we're only going to create holiday names
|
||||||
|
- the value of each `ng-model` needs to match the schema key (you can check in the `models` folder)
|
||||||
|
|
||||||
|
|
||||||
|
**In `index.html`:**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form>
|
||||||
|
<input type="text" ng-model="ctrl.createForm.name" placeholder="Holiday name"/>
|
||||||
|
<input type="submit" value="Create New Holiday"/>
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
- This is a good time to open Chrome Dev tools in your browser
|
||||||
|
|
||||||
|
## Make form make a function call
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.controller('MainController', ['$http', function ($http) {
|
||||||
|
this.h5 = 'Holidays! Celebrate!'
|
||||||
|
|
||||||
|
this.createHoliday = () => {
|
||||||
|
console.log ('submit button calls this function');
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
```
|
||||||
|
|
||||||
|
We should be able to click our `create new holiday` button ... and ... nothing should happen!
|
||||||
|
|
||||||
|
We have to add the function to our `html`
|
||||||
|
- Upgrade our opening `form` tag to the following:
|
||||||
|
|
||||||
|
**In `index.html`:**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<form ng-submit="ctrl.createHoliday()">
|
||||||
|
```
|
||||||
|
|
||||||
|
- Don't forget to refresh your browser
|
||||||
|
|
||||||
|
- Now, when we click `create new holiday`, we should see a `console.log` in our browser that reads `submit button calls this function`
|
||||||
|
|
||||||
|
- We have successfully connected our form to our `app.js` and we are able to call the right function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- Now we have to make that code do something more useful
|
||||||
|
|
||||||
|
## On form submit make AJAX request to our API/server
|
||||||
|
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
|
||||||
|
1. add a new empty object called `this.createForm`, outside of our `this.createHoliday` function
|
||||||
|
1. add an `$http` function
|
||||||
|
- Include an object as the argument that includes the following
|
||||||
|
- `method` :`'POST'`
|
||||||
|
- `url` : `/holidays`
|
||||||
|
- `data` : `this.createForm`
|
||||||
|
- Chain a `.then()` after the `$http` function, with an argument of `response`
|
||||||
|
- The `.then()` function waits for the `http` request to go through and give a response
|
||||||
|
- `.then()` function can then use what the server has responded with
|
||||||
|
- `.then()` takes two arguments, the first is a successful response from the server, the second is the error response **if** there is an error
|
||||||
|
- for now, let's just `console.log` the response
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.controller('MainController', ['$http', function ($http) {
|
||||||
|
this.h5 = 'Holidays! Celebrate!'
|
||||||
|
this.createForm = {}
|
||||||
|
|
||||||
|
|
||||||
|
this.createHoliday = function(){
|
||||||
|
$http({
|
||||||
|
method:'POST',
|
||||||
|
url: '/holidays',
|
||||||
|
data: this.createForm
|
||||||
|
}).then(response => {
|
||||||
|
console.log(response)
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}])
|
||||||
|
```
|
||||||
|
|
||||||
|
- Successful POST request `console.log`:
|
||||||
|

|
||||||
|
|
||||||
|
## Get the data from the response
|
||||||
|
|
||||||
|
The response has a lot of stuff in it. We just want the data. Let's update our `console.log` to just show our data
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.createHoliday = () => {
|
||||||
|
$http({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/holidays',
|
||||||
|
data: this.createForm
|
||||||
|
}).then(response => {
|
||||||
|
console.log(response.data)
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Success!
|
||||||
|

|
||||||
|
|
||||||
|
- We can tell this has been successfully added to our database because the `console.log` is coming from the response, we also see a mongo `id`, and also the other default values have been added in
|
||||||
|
- Nice work!
|
||||||
|
|
||||||
|
|
||||||
|
## Onwards
|
||||||
|
- go to the [angular_index.md](angular_index.md)
|
||||||
@ -0,0 +1,135 @@
|
|||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Completed `angular_index.md`
|
||||||
|
1. Update our HTML, to make room for a delete button
|
||||||
|
1. Make form make AJAX request on x click
|
||||||
|
1. get the object id and pass it in
|
||||||
|
1. On successful delete, update the page
|
||||||
|
|
||||||
|
## Update our `index.html` to have a table
|
||||||
|
|
||||||
|
A table's vertical alignment is pretty darn nice, let's use it
|
||||||
|
|
||||||
|
We'll update our table to have a cell with x which will end up being our 'delete button'
|
||||||
|
|
||||||
|
**In `index.html`**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<table>
|
||||||
|
<tr ng-repeat="holiday in ctrl.holidays">
|
||||||
|
<td> {{ holiday.name }} Day</td>
|
||||||
|
<td>x</td>
|
||||||
|
</tr>
|
||||||
|
<table>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Now our page will look like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Create a delete function inside `app.js`
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.deleteHoliday = ()=>{
|
||||||
|
console.log ("I'm going to delete you")
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Connect the `html` element to the `app.js` functionality
|
||||||
|
Add an ng-click="deleteHoliday()" to the `td` element that has the `x` in the second `td`
|
||||||
|
|
||||||
|
**In `index.html`**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<tr ng-repeat="holiday in ctrl.holidays">
|
||||||
|
<td> {{ holiday.name }} Day
|
||||||
|
<td ng-click="ctrl.deleteHoliday()">x</td>
|
||||||
|
</tr>
|
||||||
|
```
|
||||||
|
|
||||||
|
When we click the `x`, we should now get our `console.log` message
|
||||||
|
|
||||||
|
## Passing in the id to the `deleteHoliday` function
|
||||||
|
|
||||||
|
Our delete route is `holidays/mongo_id`
|
||||||
|
|
||||||
|
We need to be able to create that route in order to delete a holiday. Therefore we'll need access to the specific holiday's `_id`
|
||||||
|
|
||||||
|
Let's start by passing in that id to our `deleteHoliday` function
|
||||||
|
|
||||||
|
**In `index.html`**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<tr ng-repeat="holiday in ctrl.holidays">
|
||||||
|
<td> {{ holiday.name }} Day
|
||||||
|
<td ng-click="ctrl.deleteHoliday( holiday._id )">x</td>
|
||||||
|
</tr>
|
||||||
|
```
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
```js
|
||||||
|
this.deleteHoliday = ( id )=> {
|
||||||
|
console.log ("I'm going to delete you", id)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Calling our `$http` function for Delete Route
|
||||||
|
|
||||||
|
Since Angular is handling our routes and not the HTML, we don't need npm `method-override` to get to the correct route.
|
||||||
|
|
||||||
|
Inside our `this.deleteHoliday` function we're going to call `$http`, we'll pass it an object with the following:
|
||||||
|
- `method` : `'DELETE'`
|
||||||
|
- `url` : `/holidays/` + `id`
|
||||||
|
We can then chain `.then()`, and we'll start by just console.logging the holiday we deleted.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
In order to see our change, we'll have to refresh the page
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.deleteHoliday = id => {
|
||||||
|
$http({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: '/holidays/' + id
|
||||||
|
}).then (response => {
|
||||||
|
console.log(response.data)
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating our Change to Reflect the Change in Our Database
|
||||||
|
|
||||||
|
Again, we can take one of two approaches, we can just call `this.getHolidays()` , inside the `.then()` function.
|
||||||
|
|
||||||
|
Or we can manipulate our `holidays` array of objects.
|
||||||
|
|
||||||
|
Let's go with the latter
|
||||||
|
|
||||||
|
We're going to want to find that holiday object by its id and splice it out. In order to splice it, we'll need to know it's index position.
|
||||||
|
|
||||||
|
```js
|
||||||
|
}).then(response => {
|
||||||
|
const removeByIndex = this.holidays.findIndex(holiday => holiday._id === id)
|
||||||
|
this.holidays.splice(removeByIndex, 1)
|
||||||
|
}, error => {
|
||||||
|
```
|
||||||
|
|
||||||
|
- Let's save our file, refresh our browser and test it, let's remove a holiday.
|
||||||
|
|
||||||
|
## Takeaways
|
||||||
|
- Since Angular is handling our routes, we don't have to use method-override
|
||||||
|
- We can continue to build out the functionality we want, using good old JavaScript
|
||||||
|
|
||||||
|
|
||||||
|
## Onwards
|
||||||
|
- go to the [angular_update.md](angular_update.md)
|
||||||
@ -0,0 +1,216 @@
|
|||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Completed `angular_create.md`
|
||||||
|
1. Create a function to get all holidays
|
||||||
|
1. On page load, show all holidays
|
||||||
|
1. After creating a new holiday, refresh list of holidays
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Create a function to get all holidays
|
||||||
|
|
||||||
|
- We'll need to make a new request to get our list of all of our holidays
|
||||||
|
- Again, we'll make a new function that calls the `$http()` function
|
||||||
|
- We'll pass the `$http()` function an object with the following
|
||||||
|
- `method` : `GET`
|
||||||
|
- `url` : `/holdays`
|
||||||
|
Unlike our `POST` request, we don't need to include `data` because we're not sending any data for this get request.
|
||||||
|
|
||||||
|
- Our `$http()` function again, will have a `.then()` function chained to it, that will wait for the response from the `http` request and then give us access to that data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.getHolidays = () => {
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/holidays'
|
||||||
|
}).then(response => {
|
||||||
|
this.holidays = response.data
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If we make our changes and reload the page, we shouldn't see anything happen, no `console.log` of our data.
|
||||||
|
|
||||||
|
That's because we've only defined our function and not called it.
|
||||||
|
|
||||||
|
## Call the `getHolidays` function
|
||||||
|
|
||||||
|
We were able to call `createHoliday` because clicking on the `create new holiday` button listened for a click and then triggered this function.
|
||||||
|
|
||||||
|
In this case, we just want to immediately load our data on page load. To do this, we just invoke the function after the function has been defined.
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.getHolidays = () => {
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/holidays'
|
||||||
|
}).then(response => {
|
||||||
|
console.log(response.data)
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// load immediately on page
|
||||||
|
this.getHolidays()
|
||||||
|
```
|
||||||
|
|
||||||
|
Ok, so we are able to `console.log` on page load, which is cool, but we'd really like to update the browser view.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Render our Holidays in the Browser
|
||||||
|
|
||||||
|
With Angular's two-way data binding, we're going to have to declare a variable that is going to hold our data both in `index.html` and `app.js`
|
||||||
|
|
||||||
|
|
||||||
|
- both the `index.html` and the `app.js` will need to have this variable
|
||||||
|
- let's name our variable `holidays`
|
||||||
|
|
||||||
|
**In `index.html`**
|
||||||
|
- in our `index.html` the `holidays` variable will be accessible by adding `ctrl.` in front of it. We decided that it should be `ctrl` in our `body` tag right at the `ng-controller="MainController as ctrl"`
|
||||||
|
- so any time we want to access it in the html we need to follow two rules
|
||||||
|
- We are accessing it in a tag that is nested **inside** the `body` tag.
|
||||||
|
- We are putting `ctrl.` in front of it
|
||||||
|
- therefore our variable in `index.html` is `ctrl.holidays`
|
||||||
|
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
- We can give our `index.html` access to our `holidays` variable by doing the following
|
||||||
|
- making sure we are in the correct controller. In our case, we just have one main controller: `MainController`, so we have to make sure we are accessing our variable inside of it
|
||||||
|
- put `this.` in front of our variable, so our variable inside `app.js` inside the `MainController` is `this.holidays`
|
||||||
|
|
||||||
|
**Recap**
|
||||||
|
- It HAS to be `holidays` in both the `index.html` and `app.js`
|
||||||
|
- in `index.html` it is referenced by `ctrl.holidays`
|
||||||
|
- in `app.js` it is referenced by `this.holidays`
|
||||||
|
- changing the value in one 'place' will update the value in the other
|
||||||
|
|
||||||
|
|
||||||
|
- First we are going to set our response data to `this.holidays`
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.getHolidays = () => {
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/holidays'
|
||||||
|
}).then(response => {
|
||||||
|
this.holidays = response.data
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Next let's use `ng-repeat` to display our holiday names inside a table cell `td` tag
|
||||||
|
- `ctrl.holidays` is an array of objects
|
||||||
|
- we have to set a variable name for one holiday object, we do that with angular by writing `holiday in ctrl.holidays` - that sets a variable name of `holiday` for each object in the `holidays` array
|
||||||
|
- For now, all we want to do is see the holiday name. So we'll have to access that property out of our holiday objects by doing `holiday.name`
|
||||||
|
|
||||||
|
**In `index.html`**
|
||||||
|
|
||||||
|
```html
|
||||||
|
</form><!-- closes our create form -->
|
||||||
|
<table>
|
||||||
|
<tr ng-repeat="holiday in ctrl.holidays">
|
||||||
|
<td> {{ holiday.name }} Day</td>
|
||||||
|
</tr>
|
||||||
|
<table>
|
||||||
|
</div><!-- closes div with class container -->
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Success looks like this
|
||||||
|

|
||||||
|
|
||||||
|
## Update the List of Holidays When a New Holiday is Created
|
||||||
|
|
||||||
|
Let's add a new holiday
|
||||||
|
- We know it works because we are still `console.log`ging the response
|
||||||
|

|
||||||
|
- But we don't see our page update it
|
||||||
|
- Let's refresh our page, ok we see it now, but that's not great user experience for a user to have to know to refresh the page to see their change.
|
||||||
|
|
||||||
|
- What we really want is that upon a successful response from the database, for the page to update accordingly
|
||||||
|
|
||||||
|
- We can approach it in two ways
|
||||||
|
|
||||||
|
- In the first one, we just call `getHolidays()` again.
|
||||||
|
|
||||||
|
Let's try it, add `this.getHolidays();` inside the response of the `$http` function inside `this.createHoliday()`
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.createHoliday = () => {
|
||||||
|
$http({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/holidays',
|
||||||
|
data: this.createForm
|
||||||
|
}).then(response => {
|
||||||
|
this.getHolidays()
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Don't forget to save your file and refresh your browser
|
||||||
|
|
||||||
|
The nice thing about doing it this way is that it is super easy, just one line of code.
|
||||||
|
The downside is that it is another call to the database. For a small app that not many people use, it's not really a problem at all.
|
||||||
|
|
||||||
|
However, if you wanted to update the page without making a call to the database, you can do so by manipulating the holidays array.
|
||||||
|
|
||||||
|
Let's try it:
|
||||||
|
```js
|
||||||
|
this.createHoliday = () => {
|
||||||
|
$http({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/holidays',
|
||||||
|
data: this.createForm
|
||||||
|
}).then(response => {
|
||||||
|
// let's use unshift to put our latest holiday at the top of our list, rather than push which would add it to the bottom
|
||||||
|
this.holidays.unshift(response.data)
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can add as much functionality as you like inside the `.then()` function. Another really nice to have feature, is to empty out the form after creating a new holiday.
|
||||||
|
|
||||||
|
We can do that by setting `this.createForm` to an empty object
|
||||||
|
|
||||||
|
We can also have our new holiday appear at the top of our list by using `.unshift` instead of push
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.createHoliday = () => {
|
||||||
|
// console.log('submit button calls this function');
|
||||||
|
$http({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/holidays',
|
||||||
|
data: this.createForm
|
||||||
|
}).then(response => {
|
||||||
|
this.holidays.unshift(response.data)
|
||||||
|
this.createForm = {}
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Take aways
|
||||||
|
Even though Angular has some very specific rules and formatting, `app.js` is still Javascript and we can do as much or as little JavaScript as we like.
|
||||||
|
|
||||||
|
## Onwards
|
||||||
|
- go to the [angular_delete.md](angular_delete.md)
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Complete `install_test_api.md`
|
||||||
|
1. Set up Angular
|
||||||
|
1. Create App/Controller
|
||||||
|
|
||||||
|
|
||||||
|
## Describe the elements of the MEAN stack
|
||||||
|
|
||||||
|
MEAN stack is just a collection of tech that work well together
|
||||||
|
|
||||||
|
- Mongo
|
||||||
|
- Express
|
||||||
|
- Angular
|
||||||
|
- Node.js
|
||||||
|
|
||||||
|
## Move into working with static files
|
||||||
|
|
||||||
|
1. in the `public` directory, we'll be working with `index.html` and `/js/app.js`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Set up Angular
|
||||||
|
|
||||||
|
**In `index.html`:**
|
||||||
|
1. Include Angular cdn link in the `head` tag
|
||||||
|
1. Set up app, by including `ng-app` inside the `html` tag
|
||||||
|
1. Test {{}}, inside the `body` tag, but doing something simple like summing `2 + 2`
|
||||||
|
1. Open `localhost:3004` to test
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app>
|
||||||
|
<head>
|
||||||
|
<!-- ... -->
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js" charset="utf-8"></script>
|
||||||
|
<!-- ... -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{2+2}}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create App/Controller
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.controller('MainController', ['$http', function ($http) {
|
||||||
|
this.h5 = 'Holidays! Celebrate!'
|
||||||
|
}])
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: We may be tempted to write `()=>{}` as the callback in `app.controller`, but it won't work as expected, we have to use `function(){}` for this particular callback
|
||||||
|
|
||||||
|
**Note 2**: We are not using the $http service yet, but we will!
|
||||||
|
|
||||||
|
**In `index.html`:**
|
||||||
|
1. set `ng-app` value to `HolidaysApp` in the `html` tag
|
||||||
|
1. add script tag for `/js/app.js` inside the `head` tag, below the angular cdn
|
||||||
|
1. set `ng-controller` to `MainController` in the `body` tag in `index.html`
|
||||||
|
1. set `MainController` to have an alias of `ctrl
|
||||||
|
1. create a `div` with the class of `container`
|
||||||
|
1. remove: `{{ 2 + 2 }}`
|
||||||
|
1. Insde the `container div`, add `<h5> {{ ctrl.h5 }} </h5>` (h1 is a bit too big and distracting, despite being more semantically accurate - we can polish html/css later)
|
||||||
|
|
||||||
|
public/index.html:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="HolidaysApp">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Holiday Celebrations</title>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js" charset="utf-8"></script>
|
||||||
|
<script src="/js/app.js" charset="utf-8"></script>
|
||||||
|
<link rel="stylesheet" href="/css/normalize.css">
|
||||||
|
<link rel="stylesheet" href="/css/skeleton.css">
|
||||||
|
<link rel="stylesheet" href="/css/main.css">
|
||||||
|
</head>
|
||||||
|
<body ng-controller="MainController as ctrl">
|
||||||
|
<div class="container">
|
||||||
|
<h5> {{ ctrl.h5 }} </h5>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
**Note**: We are using an tag for speed, not sematics, best practice would be to use an h1 and use css to properly resize it. But we still have a lot of code to write, so let's see if we have time at the end to fix this.
|
||||||
|
|
||||||
|
We now should have our browser look like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Take Aways
|
||||||
|
Setting up angular takes a few steps, but going slowly and testing one step at a time can make us sure we're ready for success
|
||||||
|
|
||||||
|
## Onwards
|
||||||
|
Continue to [angular_create.md](angular_create.md)
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Completed `angular_update.md`
|
||||||
|
1. show a detailed view of one holiday on hover
|
||||||
|
|
||||||
|
## Get One Holiday Object
|
||||||
|
|
||||||
|
We'll need to set a global variable inside our `MainController`, so that we can just grab the value and then be able to update and reuse it on the functions. There are many ways to do this, but we'll just go with a simple way.
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
|
||||||
|
add `this.holiday = '';`
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.controller('MainController', ['$http', function ($http) {
|
||||||
|
this.h5 = 'Holidays! Celebrate!'
|
||||||
|
this.createForm = {}
|
||||||
|
this.holiday = ''
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, let's write a function that lets us grab this value when we hover over a holiday in the browser. We'll add a console.log just to check that it works
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
this.chooseOneHoliday = holiday => {
|
||||||
|
this.holiday = holiday
|
||||||
|
console.log(this.holiday.name)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Super! Now we just need to call this function. Let's do it on a mouseover on the table cell that has our holiday name in it
|
||||||
|
|
||||||
|
```html
|
||||||
|
|
||||||
|
<tr ng-repeat="holiday in ctrl.holidays">
|
||||||
|
<td ng-dblclick="ctrl.updateCelebrated( holiday )"
|
||||||
|
ng-class="(holiday.celebrated ? 'celebrated' : '' )"
|
||||||
|
ng-mouseover="ctrl.chooseOneHoliday( holiday )"
|
||||||
|
> {{ holiday.name }} Day</td>
|
||||||
|
<td ng-click="ctrl.deleteHoliday( holiday._id )">x</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Great! Now when we mouse over our holiday names, we should see them in our Chrome Dev tools:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This HTML will play nice with our CSS
|
||||||
|
|
||||||
|
```html
|
||||||
|
|
||||||
|
<div class="details">
|
||||||
|
<h3>Holiday Info:</h3>
|
||||||
|
<hr>
|
||||||
|
<h4> {{ ctrl.holiday.name }} Day </h4>
|
||||||
|
<h6><span> Celebrated: </span> {{ ctrl.holiday.celebrated }} </h6>
|
||||||
|
<h6> <span> Likes:</span> {{ ctrl.holiday.likes }}</h6>
|
||||||
|
<p><span> Description: </span> {{ ctrl.holiday.description }} </p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Success!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Display a Default Holiday
|
||||||
|
|
||||||
|
When we load our page, there is no default holiday, so our holiday details is empty, we can load it with the first holiday in our list.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
We may be tempted to just set `this.holiday = this.holidays[0];`
|
||||||
|
|
||||||
|
But it breaks!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This is because we are trying to load the value **before** we get a response from the server that populates our holidays array.
|
||||||
|
|
||||||
|
Let's set back to an empty string `this.holiday =''` and let's set the value inside the response of `this.getHolidays`
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.getHolidays = () => {
|
||||||
|
$http({
|
||||||
|
method: 'GET',
|
||||||
|
url: '/holidays'
|
||||||
|
}).then(response => {
|
||||||
|
this.holidays = response.data
|
||||||
|
this.holiday = this.holidays[0]
|
||||||
|
}, error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That should now load the first holiday on page load
|
||||||
|
|
||||||
|
## Take Aways
|
||||||
|
- Sometimes setting a global variable makes things easier, it isn't truly global, because it is still in our Main controller.
|
||||||
|
|
||||||
|
## Onwards
|
||||||
|
|
||||||
|
Y'all did it! You made it through the class build. Let's go work on a lab to refine our holidays app some more
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Completed `angular_delete.md`
|
||||||
|
1. change css with angular
|
||||||
|
1. simple update with double click
|
||||||
|
1. Make form make AJAX request on double click
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Changing css with angular
|
||||||
|
|
||||||
|
### Toggling a true/false value
|
||||||
|
|
||||||
|
We will want to change the appearance of the holidays we've celebrated and store this data in our database. Our Holiday object has a `celebrated` property that can be either true or false.
|
||||||
|
|
||||||
|
Let's make a new function `this.updateCelebrated` in our `app.js`
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.updateCelebrated = holiday => {
|
||||||
|
console.log ("Let's celebrate", holiday.name, 'Day!')
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
**In `index.html`**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<tr ng-repeat="holiday in ctrl.holidays">
|
||||||
|
<td ng-dblclick="ctrl.updateCelebrated( holiday )"> {{ holiday.name }} Day</td>
|
||||||
|
<td ng-click="ctrl.deleteHoliday( holiday._id )">x</td>
|
||||||
|
</tr>
|
||||||
|
```
|
||||||
|
|
||||||
|
So, now when we double click our holiday name, we should get a `console.log`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
Let's create some toggle functionality
|
||||||
|
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.updateCelebrated = holiday => {
|
||||||
|
console.log("Let's celebrate", holiday.name, 'Day!')
|
||||||
|
holiday.celebrated = !holiday.celebrated
|
||||||
|
console.log(holiday.celebrated)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, every time we double click our holiday name, the `celebrated` property toggles between true and false
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Using our true/false value to set a css class
|
||||||
|
|
||||||
|
We can base the css class on a boolean property we have in our holiday object. We have a property called `celebrated`, which by default, is false, but we can build in functionality to make it toggle between true and false.
|
||||||
|
|
||||||
|
We'll use a ternary operator. This fancy term is just a way to write a short-hand if-else statement.
|
||||||
|
|
||||||
|
A ternary operator is made up of 5 components
|
||||||
|
|
||||||
|
1. The statement to be evaluated. This statement will be evaluated if it is [truthy or falsey.](https://www.sitepoint.com/javascript-truthy-falsy/)
|
||||||
|
2. a question mark `?`
|
||||||
|
3. what happens if the statement is `truthy`
|
||||||
|
4. a `:` to separate the truthy evaluation from the falsey one
|
||||||
|
5. what happens if the statement is `falsey`
|
||||||
|
|
||||||
|
So if we have
|
||||||
|
|
||||||
|
```
|
||||||
|
(true) ? 'hooray' : 'boo'
|
||||||
|
```
|
||||||
|
This will evaluate to 'hooray'
|
||||||
|
|
||||||
|
or
|
||||||
|
```
|
||||||
|
(false) ? 'hooray' : 'boo'
|
||||||
|
```
|
||||||
|
This will evaluate to 'boo'
|
||||||
|
|
||||||
|
|
||||||
|
So, we can now dive into our `index.html` and change the `css` property based on the `celebrated` boolean value.
|
||||||
|
|
||||||
|
**In `index.html`**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<tr ng-repeat="holiday in ctrl.holidays">
|
||||||
|
<td ng-dblclick="ctrl.updateCelebrated( holiday )"
|
||||||
|
ng-class="(holiday.celebrated ? 'celebrated' : '' )"
|
||||||
|
> {{ holiday.name }} Day</td>
|
||||||
|
<td ng-click="ctrl.deleteHoliday( holiday._id )">x</td>
|
||||||
|
</tr>
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, when we refresh the page, we can double click on each holiday name and change its CSS every time, based on the `celebrated` value
|
||||||
|
|
||||||
|
### Updating our new `celebrated` value in our database
|
||||||
|
|
||||||
|
Every time we refresh our page, we lose our info of whether or not we celebrated our holiday.
|
||||||
|
|
||||||
|
We can use an `$http` call to update our database
|
||||||
|
|
||||||
|
**In `js/app.js`**
|
||||||
|
|
||||||
|
```js
|
||||||
|
this.updateCelebrated = holiday => {
|
||||||
|
holiday.celebrated = !holiday.celebrated
|
||||||
|
$http({
|
||||||
|
method: 'PUT',
|
||||||
|
url: '/holidays/' + holiday._id,
|
||||||
|
data: { celebrated: holiday.celebrated }
|
||||||
|
}).then(response => {
|
||||||
|
console.log(response.data.celebrated)
|
||||||
|
}, error => {
|
||||||
|
console.log(error.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Cool! Now we can update our database and when we reload our page it remembers if we have celebrated the holiday
|
||||||
|
|
||||||
|
## Take Aways
|
||||||
|
- Being able to manipulate css with angular adds an exciting level of interactivity
|
||||||
|
- Ternary operators are a neat way to keep our code short
|
||||||
|
- Update requires a couple extra steps, but when we make a plan and take it step by step it is not so bad
|
||||||
|
|
||||||
|
## Onwards
|
||||||
|
[Angular Show One Holiday](angular_show.md)
|
||||||
@ -0,0 +1,142 @@
|
|||||||
|
# Set Up an API
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Set up an pre-built API
|
||||||
|
1. Familiarize Yourself with the API
|
||||||
|
1. Test API
|
||||||
|
1. Onwards to full day build full CRUD app with Angular
|
||||||
|
|
||||||
|
|
||||||
|
## Today's Build
|
||||||
|
|
||||||
|
We'll be building a tiny app that let's us create, read, update and delete national holidays, using the MEAN stack. We will be starting with a completed back end.
|
||||||
|
|
||||||
|
## Define API
|
||||||
|
|
||||||
|
- An API stands for Application Program Interface
|
||||||
|
- It is a set of routines, protocols, and tools for building software applications
|
||||||
|
- It specifies how software components should interact
|
||||||
|
- Essentially it's documentation, but in the industry, it's come to mean a program or some existing software that you use to build your own app
|
||||||
|
|
||||||
|
## Set Up
|
||||||
|
|
||||||
|
1. got to today's `student_examples` and `cd` into `holdiays_app`
|
||||||
|
1. `npm i`
|
||||||
|
1. new terminal tab, start `mongod`
|
||||||
|
1. new terminal tab, start `nodemon`
|
||||||
|
1. open `Postman`, or equivalent
|
||||||
|
|
||||||
|
## Exploring the back end
|
||||||
|
|
||||||
|
Review and discuss as a class:
|
||||||
|
|
||||||
|
1. The required npm modules are __________
|
||||||
|
1. Are there any new npm modules? Any that might be missing for our needs?
|
||||||
|
1. The server has a port of __________
|
||||||
|
1. The main routes are located __________
|
||||||
|
1. In the __________ controller, the following routes are available: __________
|
||||||
|
1. When you go to localhost: __________ the following page will load __________
|
||||||
|
1. We have one model, what are the key values and data types?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Test our routes
|
||||||
|
|
||||||
|
### Using Postman (or equivalent)
|
||||||
|
- We have 4 routes to test
|
||||||
|
- Create : post /holidays : adds a new holiday
|
||||||
|
- Index : get /holidays : shows all holidays
|
||||||
|
- Update : put /holidays/id : updates a holiday
|
||||||
|
- Destroy : delete /holidays/id : deletes a holiday
|
||||||
|
|
||||||
|
### Test Create (add at least 3 holidays + 1 called 'update me')
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
[Need a holiday?](https://nationaldaycalendar.com/calendar-at-a-glance/)
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
**In Postman**
|
||||||
|
|
||||||
|
- choose `POST`
|
||||||
|
- url: `localhost:3004/holidays`
|
||||||
|
- body: `name` : `World Kindness`
|
||||||
|
- `Send`
|
||||||
|
|
||||||
|

|
||||||
|
<br>
|
||||||
|
|
||||||
|
- Expected Output
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Add two more holidays with the Post route
|
||||||
|
- Add one more holiday called `update me`
|
||||||
|
|
||||||
|
### Test Index Route
|
||||||
|
|
||||||
|
**In Postman**
|
||||||
|
|
||||||
|
- choose `GET`
|
||||||
|
- url: `localhost:3004/holidays`
|
||||||
|
- `Send`
|
||||||
|
|
||||||
|
- Expected Output
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Test Update Route
|
||||||
|
|
||||||
|
**In Postman**
|
||||||
|
|
||||||
|
**Don't leave your index route yet!**
|
||||||
|
- copy the `_id` of your `Update Me` holiday
|
||||||
|
|
||||||
|
- choose `PUT`
|
||||||
|
- url: `localhost:3004/holidays/that copied _id you got from your index route`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- body:
|
||||||
|
- `name` : `delete me`
|
||||||
|
- `celebrated` : `true`
|
||||||
|
- `Send`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Expected Output
|
||||||
|

|
||||||
|
|
||||||
|
- Do another get request to the index to double check
|
||||||
|
|
||||||
|
- Expected Output
|
||||||
|

|
||||||
|
|
||||||
|
### Test Delete Route
|
||||||
|
|
||||||
|
**In Postman**
|
||||||
|
|
||||||
|
**Don't leave your index route yet!**
|
||||||
|
|
||||||
|
- copy the `_id` of your `Update Me` holiday
|
||||||
|
|
||||||
|
- choose `DELETE`
|
||||||
|
- `url`: `localhost:3004/holidays/that copied _id you got from your index route`
|
||||||
|
|
||||||
|
- Expected Output
|
||||||
|

|
||||||
|
|
||||||
|
- Do another get request to the index to double check
|
||||||
|
|
||||||
|
- Your `delete me` holiday should be gone
|
||||||
|
|
||||||
|
## Take aways
|
||||||
|
- taking the time to familiarize yourself with the back end and then testing it allows you to:
|
||||||
|
- get familiar with the routes available
|
||||||
|
- get familiar with how the routes work
|
||||||
|
- check that the routes work as expected: As you start building your front-end, you'll run into errors. By testing the back end and knowing it works, you can better __isolate__ the source of your errors, this will make debugging easier
|
||||||
|
|
||||||
|
## Onwards
|
||||||
|
- go to the [angular_set_up.md](angular_set_up.md)
|
||||||
@ -0,0 +1,368 @@
|
|||||||
|
# MEAN Stack Auth
|
||||||
|
|
||||||
|
## Create toggle to show app vs login/signup
|
||||||
|
|
||||||
|
`index.html`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<section ng-if="ctrl.loggedInUser === false">
|
||||||
|
<h2>Sign Up</h2>
|
||||||
|
<form>
|
||||||
|
Username: <input type="text" /><br/>
|
||||||
|
Password: <input type="password" /><br/>
|
||||||
|
<input type="submit" value="Sign Up">
|
||||||
|
</form>
|
||||||
|
<h2>Log In</h2>
|
||||||
|
<form>
|
||||||
|
Username: <input type="text" /><br/>
|
||||||
|
Password: <input type="password" /><br/>
|
||||||
|
<input type="submit" value="Log In">
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
<section ng-if="ctrl.loggedInUser !== false">
|
||||||
|
<!-- previous code: ul and form -->
|
||||||
|
</section>
|
||||||
|
```
|
||||||
|
|
||||||
|
`app.js` inside controller:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.loggedInUser = false;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Make fake signup/login forms call angular methods
|
||||||
|
|
||||||
|
`app.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.signup = function(){
|
||||||
|
this.loggedInUser = {
|
||||||
|
username: 'Matthew'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.login = function(){
|
||||||
|
this.loggedInUser = {
|
||||||
|
username: 'matt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`index.html`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<section ng-if="ctrl.loggedInUser === false">
|
||||||
|
<form ng-submit="ctrl.signup()"><!-- add ng-submit -->
|
||||||
|
Username: <input type="text" /><br/>
|
||||||
|
Password: <input type="password" /><br/>
|
||||||
|
<input type="submit" value="Sign Up">
|
||||||
|
</form>
|
||||||
|
<h2>Log In</h2>
|
||||||
|
<form ng-submit="ctrl.login()"><!-- add ng-submit -->
|
||||||
|
Username: <input type="text" /><br/>
|
||||||
|
Password: <input type="password" /><br/>
|
||||||
|
<input type="submit" value="Log In">
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
<section ng-if="ctrl.loggedInUser !== false">
|
||||||
|
<h2>Welcome {{ctrl.loggedInUser.username}}</h2>
|
||||||
|
<!-- rest of code -->
|
||||||
|
</section>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create user create route for api:
|
||||||
|
|
||||||
|
create `controllers/users.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.post('/', (req, res) => {
|
||||||
|
res.json(req.body);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
```
|
||||||
|
|
||||||
|
`server.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const usersController = require('./controllers/users.js');
|
||||||
|
app.use('/users', usersController);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create User model
|
||||||
|
|
||||||
|
create `models/users.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
|
||||||
|
const userSchema = new mongoose.Schema({
|
||||||
|
username: String,
|
||||||
|
password: String
|
||||||
|
});
|
||||||
|
|
||||||
|
const User = mongoose.model('User', userSchema);
|
||||||
|
|
||||||
|
module.exports = User;
|
||||||
|
```
|
||||||
|
|
||||||
|
`controllers/users.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const User = require('../models/users.js');
|
||||||
|
|
||||||
|
router.post('/', (req, res) => {
|
||||||
|
User.create(req.body, (error, createdUser) => {
|
||||||
|
res.json(createdUser);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Add bcrypt to sign up
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install bcrypt
|
||||||
|
```
|
||||||
|
|
||||||
|
`controllers/users.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
//...
|
||||||
|
router.post('/', (req, res) => {
|
||||||
|
req.body.password = bcrypt.hashSync(req.body.password, bcrypt.genSaltSync(10));
|
||||||
|
User.create(req.body, (error, createdUser) => {
|
||||||
|
res.json(createdUser);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Make request to create user api route in angular
|
||||||
|
|
||||||
|
`index.html`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
Username: <input type="text" ng-model="ctrl.signupUsername" /><br/>
|
||||||
|
Password: <input type="password" ng-model="ctrl.signupPassword"/><br/>
|
||||||
|
```
|
||||||
|
|
||||||
|
`app.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.signup = function(){
|
||||||
|
$http({
|
||||||
|
url:'/users',
|
||||||
|
method:'POST',
|
||||||
|
data: {
|
||||||
|
username: this.signupUsername,
|
||||||
|
password: this.signupPassword
|
||||||
|
}
|
||||||
|
}).then(function(response){
|
||||||
|
controller.loggedInUser = response.data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create session create route
|
||||||
|
|
||||||
|
create `controllers/session.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const User = require('../models/users.js');
|
||||||
|
|
||||||
|
router.post('/', (req, res) => {
|
||||||
|
User.findOne({username:req.body.username}, (error, foundUser) => {
|
||||||
|
res.json(foundUser)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
```
|
||||||
|
|
||||||
|
`server.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const sessionController = require('./controllers/session.js');
|
||||||
|
app.use('/session', sessionController);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Check password
|
||||||
|
|
||||||
|
`controllers/session.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
//...
|
||||||
|
router.post('/', (req, res) => {
|
||||||
|
User.findOne({username:req.body.username}, (error, foundUser) => {
|
||||||
|
if(foundUser === null){
|
||||||
|
res.json({
|
||||||
|
message:'user not found',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const doesPasswordMatch = bcrypt.compareSync(req.body.password, foundUser.password);
|
||||||
|
if(doesPasswordMatch){
|
||||||
|
res.json(foundUser)
|
||||||
|
} else {
|
||||||
|
res.json({
|
||||||
|
message:'user not found'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integrate with Angular
|
||||||
|
|
||||||
|
```html
|
||||||
|
<h2>Log In</h2>
|
||||||
|
<form ng-submit="ctrl.login()">
|
||||||
|
Username: <input type="text" ng-model="ctrl.loginUsername"/><br/>
|
||||||
|
Password: <input type="password" ng-model="ctrl.loginPassword"/><br/>
|
||||||
|
<input type="submit" value="Log In">
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.login = function(){
|
||||||
|
$http({
|
||||||
|
url:'/session',
|
||||||
|
method:'POST',
|
||||||
|
data: {
|
||||||
|
username: this.loginUsername,
|
||||||
|
password: this.loginPassword
|
||||||
|
}
|
||||||
|
}).then(function(response){
|
||||||
|
if(response.data.username){
|
||||||
|
controller.loggedInUser = response.data;
|
||||||
|
} else {
|
||||||
|
controller.loginUsername = null;
|
||||||
|
controller.loginPassword = null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Set up sessions
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install express-session
|
||||||
|
```
|
||||||
|
|
||||||
|
`app.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const session = require('express-session');
|
||||||
|
//...
|
||||||
|
app.use(session({
|
||||||
|
secret:'feedmeseymour',
|
||||||
|
resave:false,
|
||||||
|
saveUninitialized:false
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Set session on login
|
||||||
|
|
||||||
|
`controllers/session.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const doesPasswordMatch = bcrypt.compareSync(req.body.password, foundUser.password);
|
||||||
|
if(doesPasswordMatch){
|
||||||
|
req.session.user = foundUser; //add this line
|
||||||
|
res.json(foundUser)
|
||||||
|
} else {
|
||||||
|
res.json({
|
||||||
|
message:'user not found'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Set session on sign up
|
||||||
|
|
||||||
|
`controllers/users.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
User.create(req.body, (error, createdUser) => {
|
||||||
|
req.session.user = createdUser; //add this line
|
||||||
|
res.json(createdUser);
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test to see if user is logged in on page load
|
||||||
|
|
||||||
|
`app.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// bottom of controller
|
||||||
|
this.getTodos();
|
||||||
|
|
||||||
|
$http({
|
||||||
|
method:'GET',
|
||||||
|
url:'/session'
|
||||||
|
}).then(function(response){
|
||||||
|
console.log(response);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
`controllers/session.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
router.get('/', (req, res) => {
|
||||||
|
res.json(req.session.user);
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Set session data in angular
|
||||||
|
|
||||||
|
`app.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$http({
|
||||||
|
method:'GET',
|
||||||
|
url:'/session'
|
||||||
|
}).then(function(response){
|
||||||
|
if(response.data.username){
|
||||||
|
controller.loggedInUser = response.data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Log out functionality
|
||||||
|
|
||||||
|
`controllers/session.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
router.delete('/', (req, res) => {
|
||||||
|
req.session.destroy(() => {
|
||||||
|
res.json({
|
||||||
|
destroyed:true
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
`index.html` (after welcome `h2`):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<button ng-click="ctrl.logout()">Log Out</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
`app.js`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.logout = function(){
|
||||||
|
$http({
|
||||||
|
url:'/session',
|
||||||
|
method:'DELETE'
|
||||||
|
}).then(function(){
|
||||||
|
controller.loggedInUser = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
@ -0,0 +1,113 @@
|
|||||||
|
# AngularJS - Filters
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Alter presentation of data with filters
|
||||||
|
|
||||||
|
## Alter presentation of data with filters
|
||||||
|
|
||||||
|
Often, we want to format our data a specific way for presentation purposes. Sometimes, we can just do this with plain old HTML or CSS, but sometimes we have to figure out other ways to do so. Angular filters, for example, are a great way. Angular filters are used on the view level (i.e. the `.html` file where you're displaying said data). This is because typically, views deal with cosmetic changes or formatting of data, while controllers simply deal with collecting or fetching data.
|
||||||
|
|
||||||
|
### Syntax
|
||||||
|
|
||||||
|
So, how do you filter something in Angular? The basic syntax is as follows:
|
||||||
|
|
||||||
|
```js
|
||||||
|
expression | filter
|
||||||
|
```
|
||||||
|
|
||||||
|
Where the expression is what you want to filter, and the filter is how you want to filter it.
|
||||||
|
|
||||||
|
### Built-in Filters
|
||||||
|
|
||||||
|
Angular.js has a handful of helpfully [built-in filters](https://docs.angularjs.org/api/ng/filter), for example:
|
||||||
|
|
||||||
|
1. [uppercase](https://docs.angularjs.org/api/ng/filter/uppercase) / [lowercase](https://docs.angularjs.org/api/ng/filter/lowercase)
|
||||||
|
- Uppercases or lowercases the expression
|
||||||
|
- e.g. `{{'octagon gem' | uppercase}}` would display on the page as 'OCTAGON GEM'
|
||||||
|
1. [limitTo](https://docs.angularjs.org/api/ng/filter/limitTo)
|
||||||
|
- Truncates a string to a specified amount
|
||||||
|
- e.g. `{{'My Description' | limitTo:8}}` would display on the page as 'My Descr'
|
||||||
|
1. [currency](https://docs.angularjs.org/api/ng/filter/currency)
|
||||||
|
- Formats the expression as money
|
||||||
|
- e.g. `{{product.price | currency }}` would display whatever number `product.price` is with a $ in front (for example, if product.price evaluated to 8, it would display '$8')
|
||||||
|
1. [date](https://docs.angularjs.org/api/ng/filter/date)
|
||||||
|
- Formats a JavaScript date object to whatever string format you specify
|
||||||
|
- e.g. `{{1388123412323 | date:'MM/dd/yyyy @ h:mm:ssa'}}` would display on the page as '12/26/2013 @ 9:50:12PM'
|
||||||
|
1. [orderBy](https://docs.angularjs.org/api/ng/filter/orderBy)
|
||||||
|
- Orders an array by some expression
|
||||||
|
- e.g. `<li ng-repeat="product in ctrl.products | orderBy:'price'">{{product.name}}</li>` would make the list display the products in order of increasing price
|
||||||
|
|
||||||
|
### Quick Code-along
|
||||||
|
|
||||||
|
Let's try some of these filters out! In today's `student_examples` there is some starter code for us to test these out in. Open it up and let's get started!
|
||||||
|
|
||||||
|
#### Make Name of Store Upper Case
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{ ctrl.storeName | uppercase }}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Limit How Many Letters Display on the Jingle
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{ ctrl.jingle | limitTo: 8 }}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Format the date
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{ctrl.date | date:'MM/dd/yyyy @ h:mm:ssa'}}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Format the price to currency
|
||||||
|
|
||||||
|
```js
|
||||||
|
{{product.price | currency}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using Filter to Create a Search Box
|
||||||
|
|
||||||
|
Another neat thing we can do with Angular filters is create a search box quite easily!
|
||||||
|
|
||||||
|
In order to do so, the first thing you need to do is create the input search box, then give it an `ng-model` with some sort of variable name. For our example, let's name it `searchBox`
|
||||||
|
|
||||||
|
Then, onto the html element that you want to filter through, all you have to do is simply pipe on: `filter: searchBox`, like so:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<label for="Search by">
|
||||||
|
<input type='text' placeholder='Product name' ng-model='searchBox'>
|
||||||
|
</label>
|
||||||
|
<ul>
|
||||||
|
<li ng-repeat="product in ctrl.products| filter: searchBox">{{ product.name}} :<span> {{product.price | currency}}</span></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
_NOTE:_ Your filter's ng-model variable name does _not_ have to be `searchBox`. If you want to name it differently, all you have to do is make sure that whatever you filter by matches the ng-model (i.e. `filter: variableToFilterByHere`)
|
||||||
|
|
||||||
|
## Create Sort Options
|
||||||
|
|
||||||
|
If you want the user to be able to sort a singular set of data in multiple different ways, for example letting them sort a list by low-to-high or high-to-low, or etc., you can do so utilizing `orderBy`.
|
||||||
|
|
||||||
|
For example, you can use radio inputs and set `ng-model` to some sort of variable, in our case we'll name it 'order'.
|
||||||
|
|
||||||
|
Then, for each radio input, you just have to set a `value` to however you want the data to be sorted when that radio input is selected.
|
||||||
|
|
||||||
|
Finally, onto the html element that you want to sort through dynamically, all you have to do is simply pipe on: `orderBy: order`, like so:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div>
|
||||||
|
<label for="price-up">Price Low to High</label>
|
||||||
|
<input type="radio" name="order" value="price" ng-model='order' id="price-up"/>
|
||||||
|
<label for="price-down">Price High to Low</label>
|
||||||
|
<input type="radio" name="order" value="-price" ng-model='order'id="price-down"/>
|
||||||
|
<label for="product-asc">Product Name</label>
|
||||||
|
<input type="radio" name="order" value="name" ng-model='order' id="product"/>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li ng-repeat="product in ctrl.products | orderBy: order | filter: searchBox">{{product.name}} : {{product.price | currency}}</li>
|
||||||
|
</ul>
|
||||||
|
```
|
||||||
|
|
||||||
|
_NOTE:_ Similar to the search box filter, your ng-model variable name does _not_ have to be `order`. If you want to name it differently, all you have to do is make sure that whatever you orderBy matches the ng-model (i.e. `orderBy: variableToOrderByHere`)
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
# AngularJS - Includes
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Describe why we need includes
|
||||||
|
1. Include an external file
|
||||||
|
1. Include a template already in the HTML
|
||||||
|
1. Dynamically change includes
|
||||||
|
|
||||||
|
## Describe why we need includes
|
||||||
|
|
||||||
|
Includes allow us to take reusable content and move it into external files so that we don't copy and paste the same code over and over
|
||||||
|
|
||||||
|
In terms of your group project, by separating code into different files, it will be easier to split up the work for AngularJS.
|
||||||
|
|
||||||
|
## Include an external file
|
||||||
|
|
||||||
|
Use the following syntax to include an external html file.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div ng-include="'partials/included.html'"></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note the extra single quotes!** Remember that angular directives can accept valid Javascript! So, if we want it to actually evaluate `partials/indcluded.html` as a file name string, we have to put it in single quotes. Otherwise, it'll think we want to divide two variables with JavaScript.
|
||||||
|
|
||||||
|
Now, inside this external file, you can write normal html with angular. You can even reference controllers outside of the file that are ancestors.
|
||||||
|
|
||||||
|
It does this via AJAX, but normally a browser cannot make an AJAX request to a file on a computer (because that's insecure!). So there are two ways around this if your app doesn't already have a server like express running:
|
||||||
|
|
||||||
|
- Start a basic http server from the command line
|
||||||
|
- Start up chrome with extra params
|
||||||
|
- `open /Applications/Google\ Chrome.app --args --allow-file-access-from-files`
|
||||||
|
|
||||||
|
Let's just go with the simple solution for this code-along and run an http-server
|
||||||
|
|
||||||
|
### Running Angular Server with http-server
|
||||||
|
|
||||||
|
We're going to create a basic AngularJS application _without_ an Express server for this exercise, so we need to spin up a server some other way. We want to be able to go to to [http://localhost:8080](http://localhost:8080) in our browser and see our app, but we are *not* using `nodemon` for this exercise, nor do we need `mongod`, because we do not have an express server set up. Instead, we'll run an http-server
|
||||||
|
|
||||||
|
- Install `npm-server` by running `npm install -g http-server` from the terminal.
|
||||||
|
- if you get an error about needed to be root/admin try typing `sudo !!` - that should rerun the previous command with sudo, be sure to enter your computer's password or try `sudo npm install -g http-server`
|
||||||
|
- In today's `student_examples` there should be an `includes/starter-code` folder. `cd` into it, then
|
||||||
|
- Run the server with `http-server -o`
|
||||||
|
- If the window didn't automatically open for you, go to [http://localhost:8080](http://localhost:8080) in your browser
|
||||||
|
|
||||||
|
## Water Bar Code-along
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Let's see how ng-includes work with a quick little code-along. Most of the partials are already written out, so let's just include them all!
|
||||||
|
|
||||||
|
### Include the Navigation Bar
|
||||||
|
|
||||||
|
The given file: **partials/navigation.html**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<h5>Home</h5>
|
||||||
|
<h5>About</h5>
|
||||||
|
<h5>Contact</h5>
|
||||||
|
<h5>Locations</h5>
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's include the navbar in our **index.html** at the top of the body
|
||||||
|
|
||||||
|
```html
|
||||||
|
<body ng-controller='BaseCtrl as ctrl'>
|
||||||
|
<nav ng-include="'partials/navigation'"></nav>
|
||||||
|
...
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
**REMEMBER:** the single quotes inside the double quotes around the name
|
||||||
|
|
||||||
|
Our nav bar should now appear!
|
||||||
|
|
||||||
|
### Move Our Menu Into Its Own Partial
|
||||||
|
|
||||||
|
- First, create the partial file: `touch partials/menu.html`
|
||||||
|
- Cut out the menu from our `index.html` and paste it into the partial we just created
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="menu">
|
||||||
|
<h3>Menu</h3>
|
||||||
|
<hr>
|
||||||
|
<dl ng-repeat="water in ctrl.waters">
|
||||||
|
<dt>{{water.brand}}</dt>
|
||||||
|
<dd>${{water.price}}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
- Back in our `index.html` let's now include the menu as a partial
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="container">
|
||||||
|
<div ng-include="'partials/menu'">
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dynamically Changing Includes
|
||||||
|
|
||||||
|
Again, recall that angular directives all accept valid JavaScript. This means, we can set things like `ng-include` to controller variables! Let's test this out by making it so that the area where our Menu currently is can be dynamically changed depending on where the user wants to navigate to.
|
||||||
|
|
||||||
|
In **index.html**, where we originally included our menu partial, change it to a controller variable like so:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div ng-include="ctrl.includePath"></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Thus, when we change those variables like normal, the partial will update too. Let's add some of that logic to our controller in **js/app.js**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.includePath = 'partials/menu.html';
|
||||||
|
this.changeInclude = (path) => {
|
||||||
|
this.includePath = 'partials/'+ path +'.html';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in our navbar, let's add an ng-click to each of the links so that it will run `changeInclude` every time it's clicked.
|
||||||
|
|
||||||
|
In **partials/navigation.html**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<h5 ng-click="ctrl.changeInclude('menu')">Home</h5>
|
||||||
|
<h5 ng-click="ctrl.changeInclude('about')">About</h5>
|
||||||
|
<h5 ng-click="ctrl.changeInclude('contact')">Contact</h5>
|
||||||
|
<h5 ng-click="ctrl.changeInclude('location')">Locations</h5>
|
||||||
|
```
|
||||||
@ -0,0 +1,322 @@
|
|||||||
|
# AngularJS - Custom Directives
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Explain what a custom directive is
|
||||||
|
1. Create your own custom directive
|
||||||
|
1. Include a template already in the HTML
|
||||||
|
1. Reference a controller by name
|
||||||
|
1. Get values from other attributes placed on the directive tag
|
||||||
|
|
||||||
|
## Explain what a custom directive is
|
||||||
|
|
||||||
|
You can create your own directives, just like ng-click, ng-submit, etc. This allows you to abstract away complicated/reusable code into an attribute, a class, a comment, or even your own HTML element!
|
||||||
|
|
||||||
|
## Set Up
|
||||||
|
- `mkdir custom_directives`
|
||||||
|
- `cd custom_directives`
|
||||||
|
- `touch app.js index.html product.html`
|
||||||
|
- `atom .`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js'></script>
|
||||||
|
<script type="text/javascript" src="app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create your own custom directive
|
||||||
|
|
||||||
|
Here's our custom HTML element:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<product-title></product-title>
|
||||||
|
```
|
||||||
|
|
||||||
|
the full html:
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js'></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<product-title></product-title>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's how we define its behavior:
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.directive('product', function(){
|
||||||
|
return {
|
||||||
|
restrict: 'E', // E=element, A=attribute, C=class, M=comment, can be combined
|
||||||
|
templateUrl: 'product.html', //template to replace directive element
|
||||||
|
controller(){ //controller constructor function
|
||||||
|
this.name = "arduino"
|
||||||
|
},
|
||||||
|
controllerAs: 'ctrl' //how it should be instantiated (Controller as ctrl)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE** when using multiple custom directives, or even multiple controllers, make sure they are instantiated with different aliases (e.g. ctrl1, ctrl2, etc. as opposed to ctrl, ctrl, ctrl, etc.)
|
||||||
|
|
||||||
|
Here's the external template referenced above:
|
||||||
|
|
||||||
|
file: partials/product-title.html
|
||||||
|
```html
|
||||||
|
<h3>
|
||||||
|
<p>product template</p>
|
||||||
|
This is the product name:
|
||||||
|
{{ctrl.name}}
|
||||||
|
</h3>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Include a template already in the HTML
|
||||||
|
|
||||||
|
Just like with includes, we can specify templates that are ids in our HTML. Let's make a new on for product-title
|
||||||
|
|
||||||
|
## And Reference a controller by name
|
||||||
|
You can reference a controller by name, instead of creating the controller within the directive itself
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js'></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<product></product>
|
||||||
|
<product-title></product-title>
|
||||||
|
<script type="text/ng-template" id='product-title.html'>
|
||||||
|
<h2>{{ctrl.name}}</h2>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file js/app.js
|
||||||
|
```javascript
|
||||||
|
app.directive('productTitle', function(){
|
||||||
|
return {
|
||||||
|
restrict: 'E', // E=element, A=attribute, C=class, M=comment, can be combined
|
||||||
|
templateUrl: 'product-title.html', //template to replace directive element
|
||||||
|
controller: 'MainController',
|
||||||
|
controllerAs: 'ctrl', //how it should be instantiated
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('MainController', function(){
|
||||||
|
this.name = 'main name'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Get values from other attributes placed on the directive tag
|
||||||
|
|
||||||
|
You can add other attributes onto your directive tag...
|
||||||
|
|
||||||
|
```html
|
||||||
|
<product-title awesome="super neat"></product-title>
|
||||||
|
```
|
||||||
|
|
||||||
|
and place them on your scope:
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
app.directive('productTitle', function(){
|
||||||
|
return {
|
||||||
|
restrict: 'E', // E=element, A=attribute, C=class, M=comment, can be combined
|
||||||
|
templateUrl: 'product-title.html', //template to replace directive element
|
||||||
|
controller: 'MainController',
|
||||||
|
controllerAs: 'ctrl', //how it should be instantiated
|
||||||
|
scope: {
|
||||||
|
myAttribute: '@awesome'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('MainController', function(){
|
||||||
|
this.name = 'main name'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can reference the `myAttribute` property of scope in your html
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<product></product>
|
||||||
|
<product-title awesome="super neat"></product-title>
|
||||||
|
<script type="text/ng-template" id='product-title.html'>
|
||||||
|
<h2>{{ctrl.name}}</h2>
|
||||||
|
{{myAttribute}}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
**You can change the `@` to `=` and it will attempt to evaluate the attribute's value as javascript**
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.directive('product', function(){
|
||||||
|
return {
|
||||||
|
templateUrl:'partial2.html',
|
||||||
|
controller: 'MainController',
|
||||||
|
controllerAs: 'main',
|
||||||
|
scope: {
|
||||||
|
myAttribute: '=awesome'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('MainController', function(){
|
||||||
|
this.name = 'main name'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<product></product>
|
||||||
|
<product-title awesome="super neat"></product-title>
|
||||||
|
<script type="text/ng-template" id='product-title.html'>
|
||||||
|
<h2>{{ctrl.name}}</h2>
|
||||||
|
{{myAttribute}}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
```
|
||||||
|
|
||||||
|
Lastly, you can also set the directive itself to be an attribute with a value
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<div squirrel="flying squirrel"></div>
|
||||||
|
<script type="text/ng-template" id='partial3.html'>
|
||||||
|
Template 3!!
|
||||||
|
{{myAttribute}}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
app.directive('squirrel', function(){
|
||||||
|
return {
|
||||||
|
templateUrl:'partial3.html',
|
||||||
|
controller: 'MainController',
|
||||||
|
controllerAs: 'main',
|
||||||
|
scope: {
|
||||||
|
myAttribute: '@squirrel'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('MainController', function(){
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### All the code
|
||||||
|
|
||||||
|
index.html
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.min.js'></script>
|
||||||
|
<script type="text/javascript" src="app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<product></product>
|
||||||
|
<product-title awesome="super neat"></product-title>
|
||||||
|
<script type="text/ng-template" id='product-title.html'>
|
||||||
|
<h2>{{ctrl.name}}</h2>
|
||||||
|
{{myAttribute}}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div squirrel="flying squirrel"></div>
|
||||||
|
<script type="text/ng-template" id='partial3.html'>
|
||||||
|
Template 3!!
|
||||||
|
{{myAttribute}}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
product.html
|
||||||
|
|
||||||
|
```html
|
||||||
|
<h3>
|
||||||
|
<p>product template</p>
|
||||||
|
This is the product name:
|
||||||
|
{{ctrl.name}}
|
||||||
|
</h3>
|
||||||
|
```
|
||||||
|
|
||||||
|
app.js
|
||||||
|
```js
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.directive('product', function(){
|
||||||
|
return {
|
||||||
|
restrict: 'E', // E=element, A=attribute, C=class, M=comment, can be combined
|
||||||
|
templateUrl: 'product.html', //template to replace directive element
|
||||||
|
controller(){ //controller constructor function
|
||||||
|
this.name = "arduino"
|
||||||
|
},
|
||||||
|
controllerAs: 'ctrl' //how it should be instantiated (Controller as ctrl)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
app.directive('productTitle', function(){
|
||||||
|
return {
|
||||||
|
restrict: 'E', // E=element, A=attribute, C=class, M=comment, can be combined
|
||||||
|
templateUrl: 'product-title.html', //template to replace directive element
|
||||||
|
controller: 'MainController',
|
||||||
|
controllerAs: 'ctrl', //how it should be instantiated
|
||||||
|
scope: {
|
||||||
|
myAttribute: '@awesome'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
app.directive('squirrel', function(){
|
||||||
|
return {
|
||||||
|
templateUrl:'partial3.html',
|
||||||
|
controller: 'MainController',
|
||||||
|
controllerAs: 'main',
|
||||||
|
scope: {
|
||||||
|
myAttribute: '@squirrel'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.controller('MainController', function(){
|
||||||
|
this.name = 'main name'
|
||||||
|
});
|
||||||
|
```
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
# AngularJS - Advanced
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Explain what dependencies do
|
||||||
|
1. Create a module and inject it into another one
|
||||||
|
|
||||||
|
## Explain what dependencies do
|
||||||
|
|
||||||
|
- We can group functionality together into different modules
|
||||||
|
- You can have multiple ng-apps on your page, each with specific functionality
|
||||||
|
- Modules can be dependent on other modules
|
||||||
|
- Very large modules can be broken out into smaller sub modules, grouped by functionality
|
||||||
|
|
||||||
|
## Create a module and inject it into another one
|
||||||
|
|
||||||
|
In this example, imagine we have a very large module for our "store" app. Since we don't want one very large file, we can break it apart into many smaller files.
|
||||||
|
|
||||||
|
This first file will be module just for the store directives
|
||||||
|
|
||||||
|
file: js/store-directives.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('store-directives', []);
|
||||||
|
app.directive('productTitle', function(){
|
||||||
|
// directive stuff
|
||||||
|
});
|
||||||
|
app.directive('productGallery', function(){
|
||||||
|
// directive stuff
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
We can now include the module for our directives in the store as a whole
|
||||||
|
|
||||||
|
file: js/store.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('store', ['store-directives']);
|
||||||
|
```
|
||||||
@ -0,0 +1,822 @@
|
|||||||
|
# AngularJS - Routing
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Describe Push State
|
||||||
|
1. Set up your HTML with propper tags and directives
|
||||||
|
1. Write initial JS
|
||||||
|
1. Write a basic route
|
||||||
|
1. Use url params
|
||||||
|
1. Create route specific variables
|
||||||
|
1. Create a default route
|
||||||
|
1. Use extended features of templates
|
||||||
|
|
||||||
|
## Describe Push State
|
||||||
|
|
||||||
|
You can update the browser's url without refreshing the page using JS
|
||||||
|
|
||||||
|
- this is called Push State. It's part of HTML5
|
||||||
|
- up until recently, the only way to update the url is make a new request
|
||||||
|
- click a link
|
||||||
|
- type into browser's url bar and hit enter
|
||||||
|
- now you can do that with javascript
|
||||||
|
|
||||||
|
What can Angular do with this?
|
||||||
|
|
||||||
|
- angular will listen for changes in the url
|
||||||
|
- next, angular will render a template into a specific part of the page that we define
|
||||||
|
- it will then attach a controller to this template and alias it `as` a variable which can be referenced in the HTML
|
||||||
|
|
||||||
|
## Set up your HTML with proper tags and directives
|
||||||
|
|
||||||
|
- We need to include Angular and ngRoute, which is a separate file
|
||||||
|
- This approach is similar to express, where functionality that is not crucial is moved to separate packages (ejs, method-override, etc...)
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, tell angular where to render the templates with the `ng-view` directive
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main ng-view></main><!--add ng-view here-->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
We need to make sure all links are relative to a specific base url (/ in this case). Add the following:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<head>
|
||||||
|
<!-- other head tag stuff here -->
|
||||||
|
<base href="/">
|
||||||
|
</head>
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary>file: index.html</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<base href="/"><!-- add base href here -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main ng-view></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
Links that start with `http` will not use push state. Everything else will.
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<base href="/">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">root</a></li>
|
||||||
|
<li><a href="/url1">url1 with no param</a></li>
|
||||||
|
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main ng-view></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Write initial JS
|
||||||
|
|
||||||
|
We need one dependency for our module
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `ng-app="MyApp"` to the `<body>` tag in your html
|
||||||
|
|
||||||
|
<details><summary>index.html</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
<base href="/">
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">root</a></li>
|
||||||
|
<li><a href="/url1">url1 with no param</a></li>
|
||||||
|
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main ng-view></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
We need to create a `config` for our module to make this work. A module config runs only once at load time.
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.config(function() { //.config just runs once on load. Looks like a controller, but without a name
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's add two important services
|
||||||
|
|
||||||
|
- `$locationProvider` handles push state
|
||||||
|
- push state
|
||||||
|
- allows you to update the URL in your browser without reloading the page
|
||||||
|
- also updates your history when URL changes via push state
|
||||||
|
- `$routeProvider` creates event listeners which render a template into ng-view when the browser URL matches its URL
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
//include $routeProvider and $locationProvider
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Write a basic route
|
||||||
|
|
||||||
|
Now let's add a basic route within the config function, after `$locationProvider.html5Mode({ enabled: true });`.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: function(){ // attach controller
|
||||||
|
//controller code goes here
|
||||||
|
},
|
||||||
|
controllerAs: 'ctrl' // alias for controller (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary>file: js/app.js</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl1', function(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: function(){ // attach controller
|
||||||
|
//controller code goes here
|
||||||
|
this.foo = 'bar'
|
||||||
|
},
|
||||||
|
controllerAs: 'ctrl' // alias for controller (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
add the file: partials/partial1.html
|
||||||
|
|
||||||
|
```html
|
||||||
|
Controller foo var: {{ctrl.foo}}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Now when you click the "url1 with no param" text, you should see the partial appear in the appropriate place
|
||||||
|
- Note, if you refresh the page after clicking the link, you'll get a file not found
|
||||||
|
- That's because there's no file called `url1`
|
||||||
|
- You'll have to tell express to serve up your index.html file for that route
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.get('/url1', (req, res)=>{
|
||||||
|
res.render('/public/index.html') //note the initial / so it doesn't go into views
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use url params
|
||||||
|
|
||||||
|
We can access url params with a pattern that is similar to express:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
templateUrl: 'partials/partial2.html',
|
||||||
|
controller: 'Ctrl2',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<details><summary>file: js/app.js</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl1', function(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: 'Ctrl1', // attach controller Ctrl1
|
||||||
|
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
templateUrl: 'partials/partial2.html',
|
||||||
|
controller: 'Ctrl2',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details><summary>file: index.html</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base href="/">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">root</a></li>
|
||||||
|
<li><a href="/url1">url1 with no param</a></li>
|
||||||
|
<li><a href="/url1/someid">url1 with an extra param</a></li>
|
||||||
|
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main ng-view></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
Inside the controller, we can access those url params:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
|
||||||
|
this.id = $routeParams.id; //access :id from url
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary>file: js/app.js</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
//include $routeParams service
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl1', function(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
|
||||||
|
this.id = $routeParams.id; //access :id from url
|
||||||
|
}]);
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: 'Ctrl1', // attach controller Ctrl1
|
||||||
|
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
templateUrl: 'partials/partial2.html',
|
||||||
|
controller: 'Ctrl2',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details><summary>file: partials/partial2.html</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
{{ctrl.id}}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Create route specific variables
|
||||||
|
|
||||||
|
We can create a variable for a specific route:
|
||||||
|
```javascript
|
||||||
|
$routeProvider.when('/url3', {
|
||||||
|
//... some code
|
||||||
|
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
|
||||||
|
//...some code
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary>file: js/app.js</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl1', function(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
|
||||||
|
this.id = $routeParams.id; //access :id from url
|
||||||
|
}]);
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: 'Ctrl1', // attach controller Ctrl1
|
||||||
|
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
templateUrl: 'partials/partial2.html',
|
||||||
|
controller: 'Ctrl2',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url3', {
|
||||||
|
templateUrl: 'partials/partial3.html',
|
||||||
|
controller: 'Ctrl3',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
And access it in the controller:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
|
||||||
|
this.id = $routeParams.id; //access :id from url
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary>file: js/app.js</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl1', function(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
|
||||||
|
this.id = $routeParams.id; //access :id from url
|
||||||
|
}]);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl3', ['$route', function ($route) {
|
||||||
|
this.funVar = $route.current.someVarSpecificToThisRoute; //you can access variables created in the when action like this
|
||||||
|
}]);
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: 'Ctrl1', // attach controller Ctrl1
|
||||||
|
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
templateUrl: 'partials/partial2.html',
|
||||||
|
controller: 'Ctrl2',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url3', {
|
||||||
|
templateUrl: 'partials/partial3.html',
|
||||||
|
controller: 'Ctrl3',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details><summary>add link in index.html:</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base href="/">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">root</a></li>
|
||||||
|
<li><a href="/url1">url1 with no param</a></li>
|
||||||
|
<li><a href="/url1/someid">url1 with an extra param</a></li>
|
||||||
|
<li><a href="/url3">url2</a></li>
|
||||||
|
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main ng-view></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details><summary>create partials/partial3.html</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
{{ctrl.funVar}}
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Create a default route
|
||||||
|
|
||||||
|
Lastly, you can create a default route. Note at any point, you don't have to have a controller/partial combo. You can also have just a redirect property:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$routeProvider.otherwise({ // if browser url doesn't match any of the above...
|
||||||
|
//here you can do something like above if you'd like with a template and a controller
|
||||||
|
redirectTo: '/' // or you can redirect to another url.
|
||||||
|
//redirection can happen in any 'when' action; I just happened to do it here. I could have put it in one of the above sections too
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary>file: js/app.js</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl1', function(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
|
||||||
|
this.id = $routeParams.id; //access :id from url
|
||||||
|
}]);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl3', ['$route', function ($route) {
|
||||||
|
this.funVar = $route.current.someVarSpecificToThisRoute; //you can access variables created in the when action like this
|
||||||
|
}]);
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: 'Ctrl1', // attach controller Ctrl1
|
||||||
|
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
templateUrl: 'partials/partial2.html',
|
||||||
|
controller: 'Ctrl2',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url2', {
|
||||||
|
templateUrl: 'partials/partial3.html',
|
||||||
|
controller: 'Ctrl3',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.otherwise({ // if browser url doesn't match any of the above...
|
||||||
|
//here you can do something like above if you'd like with a template and a controller
|
||||||
|
redirectTo: '/' // or you can redirect to another url.
|
||||||
|
//redirection can happen in any 'when' action; I just happened to do it here. I could have put it in one of the above sections too
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
create a route that doesn't match any predefined url:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<li><a href="/badurl">bad url</a></li>
|
||||||
|
````
|
||||||
|
|
||||||
|
<details><summary>file: index.html</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base href="/">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">root</a></li>
|
||||||
|
<li><a href="/url1">url1 with no param</a></li>
|
||||||
|
<li><a href="/url1/someid">url1 with an extra param</a></li>
|
||||||
|
<li><a href="/url2">url2</a></li>
|
||||||
|
<li><a href="/badurl">bad url</a></li>
|
||||||
|
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main ng-view></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Use extended features of templates
|
||||||
|
|
||||||
|
### Script tag templates
|
||||||
|
|
||||||
|
You can have a template inside a script tag instead of an external file.
|
||||||
|
|
||||||
|
- the advantage to this is that it doesn't need to make an extra http request to get the template
|
||||||
|
- the downside is that your html gets more bloated
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="text/ng-template" id='partials/partial4.html'>
|
||||||
|
template 4<br/>
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<details><summary>file: index.html</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base href="/">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">root</a></li>
|
||||||
|
<li><a href="/url1">url1 with no param</a></li>
|
||||||
|
<li><a href="/url1/someid">url1 with an extra param</a></li>
|
||||||
|
<li><a href="/url2">url2</a></li>
|
||||||
|
<li><a href="/badurl">bad url</a></li>
|
||||||
|
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main ng-view></main>
|
||||||
|
<script type="text/ng-template" id='partials/partial4.html'>
|
||||||
|
template 4<br/>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details><summary>file: js/app.js</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl1', function(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
|
||||||
|
this.id = $routeParams.id; //access :id from url
|
||||||
|
}]);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl3', ['$route', function ($route) {
|
||||||
|
this.funVar = $route.current.someVarSpecificToThisRoute; //you can access variables created in the when action like this
|
||||||
|
}]);
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: 'Ctrl1', // attach controller Ctrl1
|
||||||
|
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
templateUrl: 'partials/partial2.html',
|
||||||
|
controller: 'Ctrl2',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url2', {
|
||||||
|
templateUrl: 'partials/partial3.html',
|
||||||
|
controller: 'Ctrl3',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url4', {
|
||||||
|
templateUrl: 'partials/partial4.html'
|
||||||
|
})
|
||||||
|
|
||||||
|
$routeProvider.otherwise({ // if browser url doesn't match any of the above...
|
||||||
|
//here you can do something like above if you'd like with a template and a controller
|
||||||
|
redirectTo: '/' // or you can redirect to another url.
|
||||||
|
//redirection can happen in any 'when' action; I just happened to do it here. I could have put it in one of the above sections too
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
<details><summary>create link in index.html</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base href="/">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">root</a></li>
|
||||||
|
<li><a href="/url1">url1 with no param</a></li>
|
||||||
|
<li><a href="/url1/someid">url1 with an extra param</a></li>
|
||||||
|
<li><a href="/url2">url2</a></li>
|
||||||
|
<li><a href="/badurl">bad url</a></li>
|
||||||
|
<li><a href="/url4">url4</a></li>
|
||||||
|
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main ng-view></main>
|
||||||
|
<script type="text/ng-template" id='partials/partial4.html'>
|
||||||
|
template 4<br/>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Sub Partials
|
||||||
|
|
||||||
|
You can have includes inside templates.
|
||||||
|
|
||||||
|
file: partials/partial5.html
|
||||||
|
```html
|
||||||
|
template 5<br/>
|
||||||
|
<ng-include src="'partials/partial1.html'"></ng-include>
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary>create a route handler in js/app.js:</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl1', function(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
myApp.controller('Ctrl2', ['$routeParams', function ($routeParams) {
|
||||||
|
this.id = $routeParams.id; //access :id from url
|
||||||
|
}]);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl3', ['$route', function ($route) {
|
||||||
|
this.funVar = $route.current.someVarSpecificToThisRoute; //you can access variables created in the when action like this
|
||||||
|
}]);
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: 'Ctrl1', // attach controller Ctrl1
|
||||||
|
controllerAs: 'ctrl' // alias for Ctrl1 (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url1/:id', { //when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
templateUrl: 'partials/partial2.html',
|
||||||
|
controller: 'Ctrl2',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url2', {
|
||||||
|
templateUrl: 'partials/partial3.html',
|
||||||
|
controller: 'Ctrl3',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
someVarSpecificToThisRoute: 'weeee' // you can pass variables into the controller here if you'd like
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/url4', {
|
||||||
|
templateUrl: 'partials/partial4.html'
|
||||||
|
})
|
||||||
|
|
||||||
|
$routeProvider.when('/url5', {
|
||||||
|
templateUrl: 'partials/partial5.html'
|
||||||
|
})
|
||||||
|
|
||||||
|
$routeProvider.otherwise({ // if browser url doesn't match any of the above...
|
||||||
|
//here you can do something like above if you'd like with a template and a controller
|
||||||
|
redirectTo: '/' // or you can redirect to another url.
|
||||||
|
//redirection can happen in any 'when' action; I just happened to do it here. I could have put it in one of the above sections too
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details><summary>add a link in index.html:</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<base href="/">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">root</a></li>
|
||||||
|
<li><a href="/url1">url1 with no param</a></li>
|
||||||
|
<li><a href="/url1/someid">url1 with an extra param</a></li>
|
||||||
|
<li><a href="/url2">url2</a></li>
|
||||||
|
<li><a href="/badurl">bad url</a></li>
|
||||||
|
<li><a href="/url4">url4</a></li>
|
||||||
|
<li><a href="/url5">url5</a></li>
|
||||||
|
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main ng-view></main>
|
||||||
|
<script type="text/ng-template" id='partials/partial4.html'>
|
||||||
|
template 4<br/>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
@ -0,0 +1,367 @@
|
|||||||
|
# Additional Angular Topics
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Create partials
|
||||||
|
1. Create custom directives
|
||||||
|
1. Use dependencies
|
||||||
|
1. Use angular router
|
||||||
|
|
||||||
|
## Create partials
|
||||||
|
|
||||||
|
### Describe why we need includes
|
||||||
|
|
||||||
|
Includes allow us to take reusable content and move it into external files so that we don't copy and paste the same code over and over
|
||||||
|
|
||||||
|
### Include an external file
|
||||||
|
|
||||||
|
Use the following syntax to include an external html file. **Note the extra single quotes since it can take valid javascript**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div ng-include="'partials/included.html'"></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Inside this external file, you can do write normal html with angular directives. You can even reference controllers outside of the file that are ancestors.
|
||||||
|
|
||||||
|
It does this via AJAX, but normally a browser cannot make an AJAX request to a file on a computer (insecure!). Two ways around this:
|
||||||
|
|
||||||
|
- Start a basic http server from the command line
|
||||||
|
- execute this command in the same directory as application: `python -m SimpleHTTPServer` and visit [http://localhost:8000](http://localhost:8000)
|
||||||
|
- Start up chrome with extra params
|
||||||
|
- `open /Applications/Google\ Chrome.app --args --allow-file-access-from-files`
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app>
|
||||||
|
<div ng-include="'partials/included.html'"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: partials/included.html
|
||||||
|
```html
|
||||||
|
oh hai!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create custom directives
|
||||||
|
|
||||||
|
### Explain what a custom directive is
|
||||||
|
|
||||||
|
You can create your own directives, just like ng-click, ng-submit, etc. This allows you to abstract away complicated/reusable code into an attribute, a class, a comment, or even your own HTML element!
|
||||||
|
|
||||||
|
### Create your own custom directive
|
||||||
|
|
||||||
|
Here's our custom HTML element:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<product-title></product-title>
|
||||||
|
```
|
||||||
|
|
||||||
|
the full html:
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<product-title></product-title>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's how we define its behavior:
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.directive('productTitle', function(){
|
||||||
|
return {
|
||||||
|
restrict: 'E', // E=element, A=attribute, C=class, M=comment, can be combined
|
||||||
|
templateUrl: 'partials/product-title.html', //template to replace directive element
|
||||||
|
controller: function(){ //controller constructor function
|
||||||
|
this.name = "foo"
|
||||||
|
},
|
||||||
|
controllerAs: 'title' //how it should be instantiated (Controller as title)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's the external template referenced above:
|
||||||
|
|
||||||
|
file: partials/product-title.html
|
||||||
|
```html
|
||||||
|
<h3>
|
||||||
|
{{title.name}}
|
||||||
|
</h3>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use dependencies
|
||||||
|
|
||||||
|
### Explain what dependencies do
|
||||||
|
|
||||||
|
- We can group functionality together into different modules
|
||||||
|
- You can have multiple ng-apps on your page, each with specific functionality
|
||||||
|
- Modules can be dependent on other modules
|
||||||
|
- Very large modules can be broken out into smaller sub modules, grouped by functionality
|
||||||
|
|
||||||
|
### Create a module and inject it into another one
|
||||||
|
|
||||||
|
In this example, imagine we have a very large module for our "store" app. Since we don't want one very large file, we can break it apart into many smaller files.
|
||||||
|
|
||||||
|
This first file will be module just for the store directives
|
||||||
|
|
||||||
|
file: js/store-directives.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('store-directives', []);
|
||||||
|
app.directive('productTitle', function(){
|
||||||
|
// directive stuff
|
||||||
|
});
|
||||||
|
app.directive('productGallery', function(){
|
||||||
|
// directive stuff
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
We can now include the module for our directives in the store as a whole
|
||||||
|
|
||||||
|
file: js/store.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('store', ['store-directives']);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use angular router
|
||||||
|
|
||||||
|
### Describe Push State
|
||||||
|
|
||||||
|
You can update the browser's url without refreshing the page using JS
|
||||||
|
|
||||||
|
- this is called Push State. It's part of HTML5
|
||||||
|
- up until recently, the only way to update the url is make a new request
|
||||||
|
- click a link
|
||||||
|
- type into browser's url bar and hit enter
|
||||||
|
- now you can do that with javascript
|
||||||
|
|
||||||
|
What can Angular do with this?
|
||||||
|
|
||||||
|
- angular will listen for changes in the url
|
||||||
|
- next, angular will render a template into a specific part of the page that we define
|
||||||
|
- it will then attach a controller to this template and alias it `as` a variable which can be referenced in the HTML
|
||||||
|
|
||||||
|
### Set up your HTML with proper tags and directives
|
||||||
|
|
||||||
|
- We need to include Angular and ngRoute, which is a separate file
|
||||||
|
- This approach is similar to express, where functionality that is not crucial is moved to separate packages (ejs, method-override, etc...)
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, tell angular where to render the templates with the `ng-view` directive
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main ng-view></main><!--add ng-view here-->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
We need to make sure all links are relative to a specific base url (/ in this case). Add the following:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<head>
|
||||||
|
<!-- other head tag stuff here -->
|
||||||
|
<base href="/">
|
||||||
|
</head>
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary>file: index.html</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<base href="/"><!-- add base href here -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main ng-view></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
Links that start with `http` will not use push state. Everything else will.
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<base href="/">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">root</a></li>
|
||||||
|
<li><a href="/url1">url1 with no param</a></li>
|
||||||
|
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main ng-view></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Write initial JS
|
||||||
|
|
||||||
|
We need one dependency for our module
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `ng-app="MyApp"` to the `<body>` tag in your html
|
||||||
|
|
||||||
|
<details><summary>index.html</summary>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-route.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
<base href="/">
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">root</a></li>
|
||||||
|
<li><a href="/url1">url1 with no param</a></li>
|
||||||
|
<li><a href="http://www.google.com">google</a></li><!-- only this link goes outside of the app because it starts with http -->
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main ng-view></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
We need to create a `config` for our module to make this work. A module config runs only once at load time.
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.config(function() { //.config just runs once on load. Looks like a controller, but without a name
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's add two important services
|
||||||
|
|
||||||
|
- `$locationProvider` handles push state
|
||||||
|
- push state
|
||||||
|
- allows you to update the URL in your browser without reloading the page
|
||||||
|
- also updates your history when URL changes via push state
|
||||||
|
- `$routeProvider` creates event listeners which render a template into ng-view when the browser URL matches its URL
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
//include $routeProvider and $locationProvider
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Write a basic route
|
||||||
|
|
||||||
|
Now let's add a basic route within the config function, after `$locationProvider.html5Mode({ enabled: true });`.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: function(){ // attach controller
|
||||||
|
//controller code goes here
|
||||||
|
},
|
||||||
|
controllerAs: 'ctrl' // alias for controller (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
<details><summary>file: js/app.js</summary>
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const myApp = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
myApp.controller('Ctrl1', function(){
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
myApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { //.config just runs once on load
|
||||||
|
$locationProvider.html5Mode({ enabled: true }); // tell angular to use push state
|
||||||
|
|
||||||
|
$routeProvider.when('/url1', { //when http://localhost:3000/url1
|
||||||
|
templateUrl: 'partials/partial1.html', // render http://localhost:3000/partials/partial1.html
|
||||||
|
controller: function(){ // attach controller
|
||||||
|
//controller code goes here
|
||||||
|
this.foo = 'bar'
|
||||||
|
},
|
||||||
|
controllerAs: 'ctrl' // alias for controller (like ng-controller="Ctrl1 as ctrl")
|
||||||
|
});
|
||||||
|
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
add the file: partials/partial1.html
|
||||||
|
|
||||||
|
```html
|
||||||
|
Controller foo var: {{ctrl.foo}}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Now when you click the "url1 with no param" text, you should see the partial appear in the appropriate place
|
||||||
|
- Note, if you refresh the page after clicking the link, you'll get a file not found
|
||||||
|
- That's because there's no file called `url1`
|
||||||
|
- You'll have to tell express to serve up your index.html file for that route
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.get('/url1', (req, res)=>{
|
||||||
|
res.render('/public/index.html') //note the initial / so it doesn't go into views
|
||||||
|
})
|
||||||
|
```
|
||||||
@ -0,0 +1,973 @@
|
|||||||
|
# AngularJS - Routing
|
||||||
|
|
||||||
|
|
||||||
|
## Lesson Outline
|
||||||
|
|
||||||
|
We will be creating a basic page about a Vet. This will have links which load various template files via the Angular Router. Final code is available in the `instructor_examples/routing` directory, but I'll be building it temporarily in `instructor_examples/live_coding`.
|
||||||
|
|
||||||
|
1. Running Angular Server with http-server
|
||||||
|
1. Routing Overview
|
||||||
|
1. Mini exercise
|
||||||
|
1. What we are building
|
||||||
|
1. Push State Overview
|
||||||
|
1. Write initial HTML
|
||||||
|
1. Write initial JavaScript and app config
|
||||||
|
1. Enable HTML5 Push state
|
||||||
|
1. Create Contact Template
|
||||||
|
1. Create Contact Controller
|
||||||
|
1. Create Contact Route
|
||||||
|
1. Create About Template and Route
|
||||||
|
1. Pets page; Using parameter from URL, Route / Controller / Template
|
||||||
|
1. Pricing page; Pass data from Route to Template
|
||||||
|
1. Joke page; using template script tag
|
||||||
|
1. All page; nesting partials/templates
|
||||||
|
1. Default / Fallback Page
|
||||||
|
1. Google Link / External page
|
||||||
|
1. Final Code
|
||||||
|
|
||||||
|
|
||||||
|
## Running Angular Server with http-server
|
||||||
|
|
||||||
|
**Note:** Sometimes the page gets cached and updates are not seen. Try a hard refresh by holding down the shift key when you refresh
|
||||||
|
|
||||||
|
Sometimes hard refresh isn't enough, try `clear browsing history`
|
||||||
|
|
||||||
|
We're going to create a basic AngularJS application, without an Express server for this exercise. We want to be able to browser to [http://localhost:8080](http://localhost:8080). We are *not* using `nodemon` for this exercise, nor do we need `mongod`.
|
||||||
|
|
||||||
|
- Install `npm-server` by running `npm install -g http-server` from the terminal.
|
||||||
|
- Create a directory called `routing`. `mkdir routing` and `cd routing`
|
||||||
|
- Create empty `app.js` and `index.html` files inside of it. `touch app.js index.html`
|
||||||
|
- Run the server with `http-server -o`
|
||||||
|
- View the (currently blank) files by browsing to [http://localhost:8080](http://localhost:8080)
|
||||||
|
|
||||||
|
## Routing Overview
|
||||||
|
|
||||||
|
The Angular Router enables navigation from one view and controllers to the next as users perform application tasks such as clicking on links.
|
||||||
|
|
||||||
|
This is similar the routing in Express that we saw prior, and the routing that we will see in Rails in Unit 4.
|
||||||
|
|
||||||
|
Another way to think about it is when you have a specific URL, you want to have a specific section of code run.
|
||||||
|
|
||||||
|
For example, you might have `/products` to show an index of all your products, `/products/:id` to show a single product, `/login` that displays the login screen, ` /register` that displays the registration screen.
|
||||||
|
|
||||||
|
Routing will also pass the application *parameters* from the URL, such as a product id, or a search query string like `/search?query=apple`. The application can then use these parameters as a form of user input.
|
||||||
|
|
||||||
|
### Mini Exercise
|
||||||
|
|
||||||
|
Write a short list of three possible routes that an application might have, and one parameter that you might use for input. Respond in a thread here on Slack.
|
||||||
|
|
||||||
|
## What We Are Building
|
||||||
|
|
||||||
|
We're going to build the beginnings an AngularJS application, focusing almost exclusively on the routes. The view templates, controllers, HTML and CSS will be bare-bones to let us focus on this.
|
||||||
|
|
||||||
|
My cat Jade is feeling a little sick, so we're going to think about building a basic page for a Veterinarian.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Pages we need:
|
||||||
|
|
||||||
|
- Home (/)
|
||||||
|
- Contact (`/contact`)
|
||||||
|
- About (`/about`)
|
||||||
|
- Pricing (`/pricing`)
|
||||||
|
- Cats (`/pets/cats`) - Both of these will use just one route/controller/template
|
||||||
|
- Dogs (`/pets/dogs`) - Both of these will use just one route/controller/template
|
||||||
|
- Joke of the Day (`/joke`) - This will use an inline template
|
||||||
|
- All (`/all`) - This will combine multiple templates
|
||||||
|
- Bad URL (`/badurl`) - This will show a default / fallback page
|
||||||
|
- Link to Google - external link
|
||||||
|
|
||||||
|
These are a bit arbitrary and artificial of goals, but will help give us some more context to talk about specific links.
|
||||||
|
|
||||||
|
## Push State Overview
|
||||||
|
|
||||||
|
You can update the browser's url without refreshing the page using JS
|
||||||
|
|
||||||
|
- this is called Push State. It's part of HTML5
|
||||||
|
- up until recently, the only way to update the url is make a new requests
|
||||||
|
- click a link
|
||||||
|
- type into browser's url bar and hit enter
|
||||||
|
- now you can do that with javascript
|
||||||
|
|
||||||
|
What can Angular do with this?
|
||||||
|
|
||||||
|
- angular will listen for changes in the url
|
||||||
|
- next, angular will render a template into a specific part of the page that we define
|
||||||
|
- it will then attach a controller to this template and alias it `as` a variable which can be referenced in the HTML
|
||||||
|
|
||||||
|
### Write Initial HTML
|
||||||
|
|
||||||
|
We're going to setup most of the HTML we need upfront. This is a bit much to type, but this is the biggest single section of code we'll see at once here.
|
||||||
|
|
||||||
|
- We need to include Angular and [ngRoute](https://docs.angularjs.org/api/ngRoute), which is a separate file
|
||||||
|
- This approach is similar to express, where functionality that is not crucial is moved to separate packages (ejs, method-override, etc...)
|
||||||
|
- Add a link the `/contact` and other pages outlined above. You do this as you'd create any other relative link. In a moment, we'll see how to handle this with our JavaScript.
|
||||||
|
- Add a reference to our JavaScript file in the html, and add `<body ng-app="VetApp">`
|
||||||
|
- Next, tell angular where to render the templates with the `ng-view` directive
|
||||||
|
- Route relative to a base url (/ in this case) with `<base href="/">`
|
||||||
|
|
||||||
|
|
||||||
|
file: `index.html`
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.min.js" charset="utf-8"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
<base href="/" target="_blank">
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<h1>Vets and Pets!</h1>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home (/)</a></li>
|
||||||
|
<li><a href="/contact">Contact</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
<li><a href="/pricing">Pricing</a></li>
|
||||||
|
<li><a href="/joke">Joke of the Day</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Pets</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/pets/cats">Cats</a></li>
|
||||||
|
<li><a href="/pets/dogs">Dogs</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- This is the main view that will be filled in with view templates -->
|
||||||
|
<main ng-view></main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Write initial JS
|
||||||
|
|
||||||
|
We need one dependency for our module
|
||||||
|
|
||||||
|
file: `app.js`
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
```
|
||||||
|
|
||||||
|
We need to create a `config` for our module to make this work. A module config runs only once at load time.
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
//.config just runs once on load. Looks like a controller, but without a name
|
||||||
|
app.config(function() { });
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's add two important services. This is via Dependency Injection, and will perhaps seem a little odd at first.
|
||||||
|
|
||||||
|
- `$locationProvider` handles push state
|
||||||
|
- push state
|
||||||
|
- allows you to update the URL in your browser without reloading the page
|
||||||
|
- also updates your history when URL changes via push state
|
||||||
|
- `$routeProvider`
|
||||||
|
- creates event listeners which render a template into ng-view when the browser URL matches its URL
|
||||||
|
|
||||||
|
file: `app.js`
|
||||||
|
```javascript
|
||||||
|
//include $routeProvider and $locationProvider
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
|
||||||
|
// Enables Push State
|
||||||
|
$locationProvider.html5Mode({ enabled: true });
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create Contact Templates
|
||||||
|
|
||||||
|
Create a partial file that has some text inside of it, and uses the `ctrl.phone`, which we will populate and link together with the Controller and Route in the next steps
|
||||||
|
|
||||||
|
file: contact.html
|
||||||
|
```html
|
||||||
|
<h2>Contact Us:</h2>
|
||||||
|
{{ctrl.phone}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create Contact Controller
|
||||||
|
|
||||||
|
file: `app.js`
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
app.controller('ContactController', function () {
|
||||||
|
this.phone = '555-1212';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
|
||||||
|
// Enables Push State
|
||||||
|
$locationProvider.html5Mode({ enabled: true });
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create Contact Route
|
||||||
|
|
||||||
|
Now let's add a basic route within the config function. We'll use the `ContactController` we added above to tie to all together. After this, the link should show a new view when clicked on!
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$routeProvider.when('/contact', {
|
||||||
|
templateUrl: 'contact.html', // render http://localhost:3000/contact.html
|
||||||
|
controller: 'ContactController', // attach controller ContactController
|
||||||
|
controllerAs: 'ctrl' // alias for ContactController (like ng-controller="ContactController as ctrl")
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
*Question for Slack*: Why would a controller alias be useful? Respond in a threaded message.
|
||||||
|
|
||||||
|
Then we create a basic empty controller by adding `app.controller('ContactController', function() { });`. Then assign the value of `this.phone` so we can use it in the template.
|
||||||
|
|
||||||
|
file: `app.js`
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
app.controller('ContactController', function () {
|
||||||
|
this.phone = '555-1212';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
|
||||||
|
// Enables Push State
|
||||||
|
$locationProvider.html5Mode({ enabled: true });
|
||||||
|
|
||||||
|
$routeProvider.when('/contact', {
|
||||||
|
templateUrl: 'contact.html',
|
||||||
|
controller: 'ContactController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## You Turn:
|
||||||
|
|
||||||
|
Make a template, route and controller for a `/about` page. Don't worry about passing data from the controller to the template.
|
||||||
|
|
||||||
|
## Using URL Params for Individual Pets Pages
|
||||||
|
|
||||||
|
We can access url params with a pattern that is similar to express:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$routeProvider.when('/pets/:id', { // when http://localhost:3000/pets/:id - :id is a param just like in express
|
||||||
|
templateUrl: 'pets.html',
|
||||||
|
controller: 'PetController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
Inside the controller, we can access those url params using `$routeParams` that we pass to the function:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.controller('PetController', ['$routeParams', function ($routeParams) {
|
||||||
|
this.id = $routeParams.id; //access :id from url
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
These are used to pass `cats` and `dogs` from the links we created earlier in the `ul` elements, but they could be used to pass numbers or other data from the URL into the routes and controller:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<h3>Pets</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/pets/cats">Cats</a></li>
|
||||||
|
<li><a href="/pets/dogs">Dogs</a></li>
|
||||||
|
</ul>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: `app.js`
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
app.controller('ContactController', function () {
|
||||||
|
this.phone = '555-1212';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('PetController', ['$routeParams', function ($routeParams) {
|
||||||
|
// access :id from url
|
||||||
|
this.id = $routeParams.id;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
|
||||||
|
// Enables Push State
|
||||||
|
$locationProvider.html5Mode({ enabled: true });
|
||||||
|
|
||||||
|
$routeProvider.when('/contact', {
|
||||||
|
templateUrl: 'contact.html',
|
||||||
|
controller: 'ContactController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/about', {
|
||||||
|
templateUrl: 'about.html'
|
||||||
|
// We don't need to specify a controller, because there's no data
|
||||||
|
});
|
||||||
|
|
||||||
|
// when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
$routeProvider.when('/pets/:id', {
|
||||||
|
templateUrl: 'pets.html',
|
||||||
|
controller: 'PetController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create Pricing Page: Pass data from Route to Controller to Template
|
||||||
|
|
||||||
|
You can pass data from the route into the controller, and then use it in a template.
|
||||||
|
|
||||||
|
Here we pass the price of *$1 million dollars* from the route to the controller. Note the function definition uses `$route` instead of `$routeParams`!
|
||||||
|
|
||||||
|
Create a `PricingController`, route and template:
|
||||||
|
|
||||||
|
file: `app.js`
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
app.controller('ContactController', function () {
|
||||||
|
this.phone = '555-1212';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('PetController', ['$routeParams', function ($routeParams) {
|
||||||
|
// access :id from url
|
||||||
|
this.id = $routeParams.id;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('PricingController', ['$route', function ($route) {
|
||||||
|
this.price = $route.current.price;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
|
||||||
|
// Enables Push State
|
||||||
|
$locationProvider.html5Mode({ enabled: true });
|
||||||
|
|
||||||
|
$routeProvider.when('/contact', {
|
||||||
|
templateUrl: 'contact.html',
|
||||||
|
controller: 'ContactController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/about', {
|
||||||
|
templateUrl: 'about.html'
|
||||||
|
// We don't need to specify a controller, because there's no data
|
||||||
|
});
|
||||||
|
|
||||||
|
// when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
$routeProvider.when('/pets/:id', {
|
||||||
|
templateUrl: 'pets.html',
|
||||||
|
controller: 'PetController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/pricing', {
|
||||||
|
templateUrl: 'pricing.html',
|
||||||
|
controller: 'PricingController',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
price: '$1 trillion dollars'
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
file: `pricing.html`
|
||||||
|
```html
|
||||||
|
<h2>Pricing</h2>
|
||||||
|
{{ctrl.price}}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
file: `app.js`
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
app.controller('ContactController', function () {
|
||||||
|
this.phone = '555-1212';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('PetController', ['$routeParams', function ($routeParams) {
|
||||||
|
// access :id from url
|
||||||
|
this.id = $routeParams.id;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('PricingController', ['$route', function ($route) {
|
||||||
|
this.price = $route.current.price;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
|
||||||
|
// Enables Push State
|
||||||
|
$locationProvider.html5Mode({ enabled: true });
|
||||||
|
|
||||||
|
$routeProvider.when('/contact', {
|
||||||
|
templateUrl: 'contact.html',
|
||||||
|
controller: 'ContactController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/about', {
|
||||||
|
templateUrl: 'about.html'
|
||||||
|
// We don't need to specify a controller, because there's no data
|
||||||
|
});
|
||||||
|
|
||||||
|
// when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
$routeProvider.when('/pets/:id', {
|
||||||
|
templateUrl: 'pets.html',
|
||||||
|
controller: 'PetController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/pricing', {
|
||||||
|
templateUrl: 'pricing.html',
|
||||||
|
controller: 'PricingController',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
price: '$1 trillion dollars'
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Joke page; using template script tag
|
||||||
|
|
||||||
|
You can have a template inside a script tag instead of an external file.
|
||||||
|
|
||||||
|
- the advantage to this is that it doesn't need to make an extra http request to get the template
|
||||||
|
- the downside is that your html gets more bloated
|
||||||
|
|
||||||
|
Here we'll create a `joke.html` template, but instead of making a new file, we'll put the contents inside a `script` tag in `index.html`. Also, create a link to `/joke` in the navigation list.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="text/ng-template" id='joke.html'>
|
||||||
|
<h2>Pet Joke of the Day<h2/>
|
||||||
|
<p>{{ctrl.joke}}</p>
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: `index.html`
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.min.js" charset="utf-8"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
<base href="/" target="_blank">
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<h1>Vets and Pets!</h1>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home (/)</a></li>
|
||||||
|
<li><a href="/contact">Contact</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
<li><a href="/pricing">Pricing</a></li>
|
||||||
|
<li><a href="/joke">Joke of the Day</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Pets</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/pets/cats">Cats</a></li>
|
||||||
|
<li><a href="/pets/dogs">Dogs</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- This is the main view that will be filled in with view templates -->
|
||||||
|
<main ng-view></main>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id='joke.html'>
|
||||||
|
<h2>Pet Joke of the Day</h2>
|
||||||
|
<p>{{ctrl.joke}}</p>
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, create a `JokeController` and `/joke` route. Also, create an array of jokes in the `JokeController`, and pass the first one to `this.joke` so that the joke is displayed in the template.
|
||||||
|
|
||||||
|
If you have a moment of extra time, try to figure out how to make a random joke appear each time on the page.
|
||||||
|
|
||||||
|
|
||||||
|
file `app.js`
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
app.controller('ContactController', function () {
|
||||||
|
this.phone = '555-1212';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('PetController', ['$routeParams', function ($routeParams) {
|
||||||
|
// access :id from url
|
||||||
|
this.id = $routeParams.id;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('JokeController', ['$route', function () {
|
||||||
|
this.joke = "Why don't cats like online shopping? They prefere a cat-alogue"
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('PricingController', ['$route', function ($route) {
|
||||||
|
this.price = $route.current.price;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
|
||||||
|
// Enables Push State
|
||||||
|
$locationProvider.html5Mode({ enabled: true });
|
||||||
|
|
||||||
|
$routeProvider.when('/contact', {
|
||||||
|
templateUrl: 'contact.html',
|
||||||
|
controller: 'ContactController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/about', {
|
||||||
|
templateUrl: 'about.html'
|
||||||
|
// We don't need to specify a controller, because there's no data
|
||||||
|
});
|
||||||
|
|
||||||
|
// when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
$routeProvider.when('/pets/:id', {
|
||||||
|
templateUrl: 'pets.html',
|
||||||
|
controller: 'PetController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/pricing', {
|
||||||
|
templateUrl: 'pricing.html',
|
||||||
|
controller: 'PricingController',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
price: '$1 trillion dollars'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/joke', {
|
||||||
|
templateUrl: 'joke.html',
|
||||||
|
controller: 'JokeController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## All Page - Nesting Partials/Templates
|
||||||
|
|
||||||
|
Combining multiple partials is possible by having multiple `ng-include` elements in an html file. This is a great way to reuse code. Our example here is a bit artificial, but will show you how to reuse partials we've already created above all together.
|
||||||
|
|
||||||
|
Create an `all.html` file, and make `ng-include` elements for `contact.html`, `pricing.html` and `joke.html`.
|
||||||
|
|
||||||
|
file: `all.html`
|
||||||
|
```html
|
||||||
|
<ng-include src="contact.html"></ng-include>
|
||||||
|
<ng-include src="pricing.html"></ng-include>
|
||||||
|
<ng-include src="joke.html"></ng-include>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then create a link to `/all` in the `index.html`
|
||||||
|
|
||||||
|
file: `index.html`
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.min.js" charset="utf-8"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
<base href="/" target="_blank">
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<h1>Vets and Pets!</h1>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home (/)</a></li>
|
||||||
|
<li><a href="/contact">Contact</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
<li><a href="/pricing">Pricing</a></li>
|
||||||
|
<li><a href="/joke">Joke of the Day</a></li>
|
||||||
|
<li><a href="/all">Contact, Pricing and Joke!</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Pets</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/pets/cats">Cats</a></li>
|
||||||
|
<li><a href="/pets/dogs">Dogs</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- This is the main view that will be filled in with view templates -->
|
||||||
|
<main ng-view></main>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id='joke.html'>
|
||||||
|
<h2>Pet Joke of the Day</h2>
|
||||||
|
<p>{{ctrl.joke}}</p>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
Create an `AllController` that will give us the data needed for those partials. There are cleaner ways of not-repeating ourselves for this data, but for now let's just repeat the code to keep things simple.
|
||||||
|
|
||||||
|
Then, create a route to `/all` that will use the `AllController` and the `all.html` partial.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
app.controller('ContactController', function () {
|
||||||
|
this.phone = '555-1212';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('PetController', ['$routeParams', function ($routeParams) {
|
||||||
|
// access :id from url
|
||||||
|
this.id = $routeParams.id;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('JokeController', ['$route', function () {
|
||||||
|
const jokes = ["Why don't cats like dogs?",
|
||||||
|
"What is the difference between a duck?"];
|
||||||
|
|
||||||
|
//you can access variables created in the when action like this
|
||||||
|
this.joke = jokes[0];
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('PricingController', ['$route', function ($route) {
|
||||||
|
this.price = $route.current.price;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('AllController', ['$route', function () {
|
||||||
|
const jokes = ["Why don't cats like dogs?",
|
||||||
|
"What is the difference between a duck?"];
|
||||||
|
|
||||||
|
this.joke = jokes[1];
|
||||||
|
this.phone = '555-1212';
|
||||||
|
this.price = '$1 billion dollars';
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
|
||||||
|
// Enables Push State
|
||||||
|
$locationProvider.html5Mode({ enabled: true });
|
||||||
|
|
||||||
|
$routeProvider.when('/contact', {
|
||||||
|
templateUrl: 'contact.html',
|
||||||
|
controller: 'ContactController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/about', {
|
||||||
|
templateUrl: 'about.html'
|
||||||
|
// We don't need to specify a controller, because there's no data
|
||||||
|
});
|
||||||
|
|
||||||
|
// when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
$routeProvider.when('/pets/:id', {
|
||||||
|
templateUrl: 'pets.html',
|
||||||
|
controller: 'PetController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/pricing', {
|
||||||
|
templateUrl: 'pricing.html',
|
||||||
|
controller: 'PricingController',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
price: '$1 trillion dollars'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/joke', {
|
||||||
|
templateUrl: 'joke.html',
|
||||||
|
controller: 'JokeController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/all', {
|
||||||
|
templateUrl: 'all.html',
|
||||||
|
controller: 'AllController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Default / Fallback routes
|
||||||
|
|
||||||
|
What if we have a link that doesn't match the routes defined? We can set a catch-all default route in that case using `otherwise`. You can either have a specific controller and template you'd like to use, or you can redirect it with `redirectTo` in the `app.js` file.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$routeProvider.otherwise({
|
||||||
|
redirectTo: '/'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
In `index.html` then you can put a link do a page that otherwise doesn't exist:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<a href="/badurl">/badurl</a>
|
||||||
|
```
|
||||||
|
|
||||||
|
Afterward, `app.js` and `index.html` should look like these:
|
||||||
|
|
||||||
|
file: `app.js`
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
app.controller('ContactController', function () {
|
||||||
|
this.phone = '555-1212';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('PetController', ['$routeParams', function ($routeParams) {
|
||||||
|
// access :id from url
|
||||||
|
this.id = $routeParams.id;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('JokeController', ['$route', function () {
|
||||||
|
const jokes = ["Why don't cats like dogs?",
|
||||||
|
"What is the difference between a duck?"];
|
||||||
|
|
||||||
|
//you can access variables created in the when action like this
|
||||||
|
this.joke = jokes[0];
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('PricingController', ['$route', function ($route) {
|
||||||
|
this.price = $route.current.price;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('AllController', ['$route', function () {
|
||||||
|
const jokes = ["Why don't cats like dogs?",
|
||||||
|
"What is the difference between a duck?"];
|
||||||
|
|
||||||
|
this.joke = jokes[1];
|
||||||
|
this.phone = '555-1212';
|
||||||
|
this.price = '$1 billion dollars';
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
|
||||||
|
// Enables Push State
|
||||||
|
$locationProvider.html5Mode({ enabled: true });
|
||||||
|
|
||||||
|
$routeProvider.when('/contact', {
|
||||||
|
templateUrl: 'contact.html',
|
||||||
|
controller: 'ContactController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/about', {
|
||||||
|
templateUrl: 'about.html'
|
||||||
|
// We don't need to specify a controller, because there's no data
|
||||||
|
});
|
||||||
|
|
||||||
|
// when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
$routeProvider.when('/pets/:id', {
|
||||||
|
templateUrl: 'pets.html',
|
||||||
|
controller: 'PetController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/pricing', {
|
||||||
|
templateUrl: 'pricing.html',
|
||||||
|
controller: 'PricingController',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
price: '$1 trillion dollars'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/joke', {
|
||||||
|
templateUrl: 'joke.html',
|
||||||
|
controller: 'JokeController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/all', {
|
||||||
|
templateUrl: 'all.html',
|
||||||
|
controller: 'AllController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.otherwise({
|
||||||
|
redirectTo: '/'
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
file: `index.html`
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.min.js" charset="utf-8"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
<base href="/" target="_blank">
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<h1>Vets and Pets!</h1>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home (/)</a></li>
|
||||||
|
<li><a href="/contact">Contact</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
<li><a href="/pricing">Pricing</a></li>
|
||||||
|
<li><a href="/joke">Joke of the Day</a></li>
|
||||||
|
<li><a href="/all">Contact, Pricing and Joke!</a></li>
|
||||||
|
<li><a href="/badurl">/badurl</a>, redirects to default home page (/)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Pets</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/pets/cats">Cats</a></li>
|
||||||
|
<li><a href="/pets/dogs">Dogs</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- This is the main view that will be filled in with view templates -->
|
||||||
|
<main ng-view></main>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id='joke.html'>
|
||||||
|
<h2>Pet Joke of the Day</h2>
|
||||||
|
<p>{{ctrl.joke}}</p>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Google Link / External Pages
|
||||||
|
|
||||||
|
If we include a link to another site using `http`, instead of `https` it skips the AngularJS Routes and simply opens a new tab/window.
|
||||||
|
|
||||||
|
We do this by just adding a standard link in the `index.html`
|
||||||
|
```html
|
||||||
|
<li><a href="http://www.google.com">google</a></li>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Final code
|
||||||
|
|
||||||
|
Here are our main two files, `index.html` and `app.js`. The partial files can be found in `instructor_examples/routing`
|
||||||
|
|
||||||
|
file: `index.html`
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title></title>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js" charset="utf-8"></script>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.min.js" charset="utf-8"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
<base href="/" target="_blank">
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<h1>Vets and Pets!</h1>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home (/)</a></li>
|
||||||
|
<li><a href="/contact">Contact</a></li>
|
||||||
|
<li><a href="/about">About</a></li>
|
||||||
|
<li><a href="/pricing">Pricing</a></li>
|
||||||
|
<li><a href="/joke">Joke of the Day</a></li>
|
||||||
|
<li><a href="/all">Contact, Pricing and Joke!</a></li>
|
||||||
|
<li><a href="/badurl">/badurl</a>, redirects to default home page (/)</li>
|
||||||
|
|
||||||
|
<!-- only this link skips the router because it starts with http -->
|
||||||
|
<li><a href="http://www.google.com">google</a> loads external site</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Pets</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/pets/cats">Cats</a></li>
|
||||||
|
<li><a href="/pets/dogs">Dogs</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- This is the main view that will be filled in with view templates -->
|
||||||
|
<main ng-view></main>
|
||||||
|
|
||||||
|
<script type="text/ng-template" id='joke.html'>
|
||||||
|
<h2>Pet Joke of the Day</h2>
|
||||||
|
<p>{{ctrl.joke}}</p>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: `app.js`
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', ['ngRoute']);
|
||||||
|
|
||||||
|
app.controller('ContactController', function () {
|
||||||
|
this.phone = '555-1212';
|
||||||
|
});
|
||||||
|
|
||||||
|
app.controller('PetController', ['$routeParams', function ($routeParams) {
|
||||||
|
// access :id from url
|
||||||
|
this.id = $routeParams.id;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('JokeController', ['$route', function () {
|
||||||
|
const jokes = ["Why don't cats like dogs?",
|
||||||
|
"What is the difference between a duck?"];
|
||||||
|
|
||||||
|
//you can access variables created in the when action like this
|
||||||
|
this.joke = jokes[0];
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('PricingController', ['$route', function ($route) {
|
||||||
|
this.price = $route.current.price;
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('AllController', ['$route', function () {
|
||||||
|
const jokes = ["Why don't cats like dogs?",
|
||||||
|
"What is the difference between a duck?"];
|
||||||
|
|
||||||
|
this.joke = jokes[1];
|
||||||
|
this.phone = '555-1212';
|
||||||
|
this.price = '$1 billion dollars';
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.config(['$routeProvider','$locationProvider', function($routeProvider,$locationProvider) {
|
||||||
|
// Enables Push State
|
||||||
|
$locationProvider.html5Mode({ enabled: true });
|
||||||
|
|
||||||
|
$routeProvider.when('/contact', {
|
||||||
|
templateUrl: 'contact.html',
|
||||||
|
controller: 'ContactController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/about', {
|
||||||
|
templateUrl: 'about.html'
|
||||||
|
// We don't need to specify a controller, because there's no data
|
||||||
|
});
|
||||||
|
|
||||||
|
// when http://localhost:3000/url1/:id - :id is a param just like in express
|
||||||
|
$routeProvider.when('/pets/:id', {
|
||||||
|
templateUrl: 'pets.html',
|
||||||
|
controller: 'PetController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/pricing', {
|
||||||
|
templateUrl: 'pricing.html',
|
||||||
|
controller: 'PricingController',
|
||||||
|
controllerAs: 'ctrl',
|
||||||
|
price: '$1 trillion dollars'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/joke', {
|
||||||
|
templateUrl: 'joke.html',
|
||||||
|
controller: 'JokeController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.when('/all', {
|
||||||
|
templateUrl: 'all.html',
|
||||||
|
controller: 'AllController',
|
||||||
|
controllerAs: 'ctrl'
|
||||||
|
});
|
||||||
|
|
||||||
|
$routeProvider.otherwise({
|
||||||
|
// if browser url doesn't match any of the above...
|
||||||
|
// here you can do something like above if you'd like with a template and a controller
|
||||||
|
redirectTo: '/' // or you can redirect to another url.
|
||||||
|
// redirection can happen in any 'when' action; I just happened to do it here.
|
||||||
|
// I could have put it in one of the above sections too
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
# Scope
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Pass variables from controller to view using $scope
|
||||||
|
1. Travel around the controller tree using $scope properties
|
||||||
|
|
||||||
|
## Pass variables from controller to view using $scope
|
||||||
|
|
||||||
|
- the glue between controllers and views
|
||||||
|
- an object that gets properties added to it
|
||||||
|
- each property turns into a variable in the view
|
||||||
|
- just like `res.render('view.ejs', {variable1:'foo'})`
|
||||||
|
|
||||||
|
file: app.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('BaseCtrl', ['$scope', function($scope){
|
||||||
|
$scope.foo = 'bar';
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
file:index.html
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="MyApp">
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div ng-controller="BaseCtrl as base">
|
||||||
|
<span ng-bind="foo"></span>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Travel around the controller tree using $scope properties
|
||||||
|
|
||||||
|
You can also access parent, child, and sibling controller properties via $scope's `$parent`, `$$childHead`, `$$childTail`, `$$nextSbling`, `$$prevSibling` properties
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html ng-app="MyApp">
|
||||||
|
<head>
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
|
||||||
|
<script src="app.js" charset="utf-8"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-controller="ParentCtrl as parent">
|
||||||
|
<div ng-controller="ChildController as child1">
|
||||||
|
<button ng-click="child1.getProps();">Click Me</button>
|
||||||
|
</div>
|
||||||
|
<div ng-controller="ChildController as child2">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div ng-controller="ChildController as child3">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: app.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('ParentCtrl', ['$scope', function($scope){
|
||||||
|
this.prop1 = 'foo';
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('ChildController', ['$scope', function($scope){
|
||||||
|
this.getProps = function(){
|
||||||
|
console.log($scope.$parent); //get parent scope
|
||||||
|
console.log($scope.$parent.parent.prop1); //get prop1 on controller instance
|
||||||
|
console.log($scope.$parent.$$childHead); //get first child of parent
|
||||||
|
console.log($scope.$parent.$$childHead.$$nextSibling); //get first sibling of first child of parent
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
```
|
||||||
@ -0,0 +1,200 @@
|
|||||||
|
# AngularJS - Watch and Apply
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Use $watch to listen for changes on variables
|
||||||
|
1. Use $apply to force a redraw of the page
|
||||||
|
|
||||||
|
## Use $watch to listen for changes on variables
|
||||||
|
|
||||||
|
### Use 1
|
||||||
|
|
||||||
|
We can create event listeners for any property of the $scope variable. This can be useful if you have a scope variable that changes frequently from multiple sources, but you want to perform the same action each time it updates.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$scope.$watch('somevar', function(newValue, oldValue){
|
||||||
|
//will run whenever $scope.somevar changes
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 1
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<main ng-controller="MainController as main">
|
||||||
|
{{somevar}}
|
||||||
|
<button ng-click="main.changeScopeVar();">Click Me</button>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('MainController', ['$scope', function($scope){
|
||||||
|
$scope.somevar = 'foo';
|
||||||
|
|
||||||
|
$scope.$watch('somevar', function(newValue, oldValue){
|
||||||
|
//will run whenever $scope.somevar changes
|
||||||
|
console.log(newValue, oldValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.changeScopeVar = function(){
|
||||||
|
$scope.somevar = 'bar';
|
||||||
|
};
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use 2
|
||||||
|
|
||||||
|
If no string is passed as first parameter. Entire scope is watched. This is useful for controller properties when controller alias is not known.
|
||||||
|
|
||||||
|
**NOTE** that the two parameters for the old value and new value of $scope will appear identical, since they both point to the same object in memory.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$scope.$watch(function(newValue, oldValue){
|
||||||
|
//will run whenever $scope changes
|
||||||
|
//NOTE: newValue and oldValue will have same property values, since both point the same object, which has been modified
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<main ng-controller="MainController as main">
|
||||||
|
{{somevar}}
|
||||||
|
<button ng-click="main.changeScopeVar();">Click Me</button>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('MainController', ['$scope', function($scope){
|
||||||
|
$scope.somevar = 'foo';
|
||||||
|
|
||||||
|
$scope.$watch(function(newValue, oldValue){
|
||||||
|
//will run whenever $scope.somevar changes
|
||||||
|
console.log(newValue, oldValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.changeScopeVar = function(){
|
||||||
|
$scope.somevar = 'bar';
|
||||||
|
};
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use 3
|
||||||
|
|
||||||
|
You can also watch any variables by passing in a function as the first param that returns the variable to be watched
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$scope.$watch(function(){ return foo; }, function(newValue, oldValue){
|
||||||
|
//will fire when global foo variable is changed
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 3
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<main ng-controller="MainController as main">
|
||||||
|
<button ng-click="main.clickFunction()">Click Me</button>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
const someThirdPartyVariable = 'foo';
|
||||||
|
|
||||||
|
app.controller('MainController', ['$scope', function($scope){
|
||||||
|
$scope.$watch(
|
||||||
|
function(){ return someThirdPartyVariable; },
|
||||||
|
function(newValue, oldValue){
|
||||||
|
//will fire when global foo variable is changed
|
||||||
|
console.log(newValue, oldValue);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.clickFunction = function(){
|
||||||
|
someThirdPartyVariable = 'bar';
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use $apply to force a redraw of the page
|
||||||
|
|
||||||
|
Angular doesn't watch $scope properties constantly for changes. This would be processing intensive. Instead, it has listeners for events like ng-click, ng-submit, etc. When these fire, Angular looks at the scopes to see if anything changes. If there's a change, Angular will call the $digest cycle, which basically means it will redraw the view.
|
||||||
|
|
||||||
|
If you change a $scope property outside of the Angular environment (perhaps through a third party JS library), the view won't refresh. You can manually force the $digest cycle, though. To do this, simply call:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$scope.$apply(function(){
|
||||||
|
//once the code in here completes, force the $digest cycle to run
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body ng-app="MyApp">
|
||||||
|
<main ng-controller="MainController as main">
|
||||||
|
{{foo}}
|
||||||
|
<button>Click Me</button>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('MainController', ['$scope', function($scope){
|
||||||
|
$scope.foo = 'foo';
|
||||||
|
|
||||||
|
document.querySelector('button').addEventListener('click', function(){
|
||||||
|
$scope.$apply(function(){
|
||||||
|
$scope.foo = 'bar';
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}]);
|
||||||
|
```
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
# AngularJS - Events
|
||||||
|
|
||||||
|
## Lesson Objectives
|
||||||
|
|
||||||
|
1. Describe situations where communicating between controllers is difficult
|
||||||
|
1. Send and receive an event
|
||||||
|
|
||||||
|
## Describe situations where communicating between controllers is difficult
|
||||||
|
|
||||||
|
- We want to modularize our code, so that when a controller performs an action, it can notify all of its ancestors/descendants
|
||||||
|
- This allows a developer to work on his/her specific module, without worrying about what's going on around them
|
||||||
|
- When elements with controllers are being created dynamically, knowing a controller's ancestors/descendants can become difficult.
|
||||||
|
|
||||||
|
## Send and receive an event
|
||||||
|
|
||||||
|
To send a message down to your descendant controllers:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$scope.$broadcast('eventName', { message: msg });
|
||||||
|
```
|
||||||
|
|
||||||
|
To send a message up to your ancestor controllers:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$scope.$emit('eventName', { message: msg });
|
||||||
|
```
|
||||||
|
|
||||||
|
To receive a message sent by another controller:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
$scope.$on('eventName', function (event, data) {
|
||||||
|
//do stuff here
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Broadcast
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main ng-app="MyApp">
|
||||||
|
<h1>Hi</h1>
|
||||||
|
<section ng-controller="ParentCtrl as parent">
|
||||||
|
<a href="#" ng-click="parent.sendMessage()">Click to Send Message</a>
|
||||||
|
<div ng-controller="ChildCtrl as child"></div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
|
||||||
|
app.controller('ParentCtrl', ['$scope', function($scope){
|
||||||
|
this.sendMessage = function(){
|
||||||
|
$scope.$broadcast('eventName', { someProperty:'somevalue' });
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('ChildCtrl', ['$scope', function($scope){
|
||||||
|
$scope.$on('eventName', function(event, data){
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Emit
|
||||||
|
|
||||||
|
file: index.html
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html >
|
||||||
|
<head>
|
||||||
|
<script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js'></script>
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main ng-app="MyApp">
|
||||||
|
<h1>Hi</h1>
|
||||||
|
<section ng-controller="ParentCtrl as parent">
|
||||||
|
<div ng-controller="ChildCtrl as child">
|
||||||
|
<a href="#" ng-click="child.sendMessage()">Click Me</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
file: js/app.js
|
||||||
|
```javascript
|
||||||
|
const app = angular.module('MyApp', []);
|
||||||
|
|
||||||
|
app.controller('ParentCtrl', ['$scope', function($scope){
|
||||||
|
$scope.$on('eventName', function(event, data){
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
|
}]);
|
||||||
|
|
||||||
|
app.controller('ChildCtrl', ['$scope', function($scope){
|
||||||
|
this.sendMessage = function(){
|
||||||
|
$scope.$emit('eventName', { someProperty:'somevalue' });
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
```
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
# AngularJS Potpourri
|
||||||
|
|
||||||
|
Our last day of AngularJS will go over
|
||||||
|
- Filters (in custom_directives)
|
||||||
|
- Includes (in custom_directives)
|
||||||
|
|
||||||
|
The above two are the two lessons that will likely be the most useful for student projects.
|
||||||
|
|
||||||
|
Rather than learn technologies and try to build an app that shoehorns in everything you've learned, a better approach is to figure out what you want to build, apply your fundamental skills and then teach yourself what you need to know in order for your project to function.
|
||||||
|
|
||||||
|
**Brief Summary of other topics/notes:**
|
||||||
|
|
||||||
|
- **Custom Directives:** you have `ng-click`, `ng-repeat` and a lot of directives available to you, but perhaps you are writing a lot of repetitive code that doesn't quite have a directive that meets your specific needs? You can write your own!
|
||||||
|
|
||||||
|
- **Dependencies:** Is your app getting massive? Are there many parts to it? Break it down into modules and add them in as dependencies
|
||||||
|
|
||||||
|
- **Routing:** wish you could use the forward and back buttons on your browser? Wish you could send someone a link to a specific Angular view? Make these wishes come true but using AngularJS routing
|
||||||
|
|
||||||
|
- **Scope:** Did you end up with multiple controllers? Do they need to share information/pass it around? Check out AngularJS's scope
|
||||||
|
|
||||||
|
- **Watch/Apply:** AngularJS is listening for events that you've added. Once you've clicked on something with an event listener, AngularJS will go through and update all the values needed (AngularJS digest loop). But perhaps you need to _watch_ for another event and _apply_ changes. (A simple example is a stopwatch, you want the value to increase automatically, without waiting for someone to keep clicking something/typing on the keyboard etc). This might be what you need to get it done
|
||||||
|
|
||||||
|
- **Events:** More than one controller? Do these controllers need to be aware of certain actions happening in the other controller(s)? Check out Events, to see if this can solve the problems you are looking for
|
||||||
|
|
||||||
|
**Final Caveat**
|
||||||
|
- AngularJS is great! However, it grew into something much bigger and solving many more problems that ever was intended. Therefore some of these more advanced things can be cumbersome to work with. Newer frameworks like the new Angular, React, Vue etc. took into consideration a lot of the problems that AngularJS wasn't able to solve elegantly as a first generation framework. So if you find yourself frustrated applying any of the above, don't assume every framework will be just as frustrating with their solutions to things like routing.
|
||||||
Loading…
Reference in new issue