You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

10 KiB

Additional Angular Topics

Lesson Objectives

  1. Create partials
  2. Create custom directives
  3. Use dependencies
  4. 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

<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
  • Start up chrome with extra params
    • open /Applications/Google\ Chrome.app --args --allow-file-access-from-files

Example

file: index.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

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:

<product-title></product-title>

the full html:

file: index.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

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

<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

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

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

<!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

<!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:

<head>
	<!-- other head tag stuff here -->
	<base href="/">
</head>
file: index.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>

Links that start with http will not use push state. Everything else will.

file: index.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

const myApp = angular.module('MyApp', ['ngRoute']);

Add ng-app="MyApp" to the <body> tag in your html

index.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>

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

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

//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 });.

$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")
});
file: js/app.js
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")
	});

}]);

add the file: partials/partial1.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

      app.get('/url1', (req, res)=>{
      	res.render('/public/index.html') //note the initial / so it doesn't go into views
      })