Component Based Hello World

Angular applications are built out of components. A component works like a brand new type of DOM node which we invent. It can have behaviour, it can emit events, it can have custom attributes, it holds its own state.

In this section we'll build the simplest possible "Hello World" component using regular ol' JavaScript.

The JavaScripts

First up we need some JavaScript. We currently need four files to make our simple app work. These are:

  1. angular2-polyfills.js
  2. Rx.umd.js
  3. angular2-all.umd.js
  4. app.js

angular2-polyfills.js

Angular uses edge JavaScript, things like Reflect for example that are not available in any current browser. The polyfills script makes these features available.

Rx.umd.js

Angular uses Observables for AJAX. Observables are like Promises, but with a much fuller API.

angular2-all.umd.js

Angular 2 UMD - the Universal Module Definition version of Angular exposes the Angular internals on a glbal variable called ng. This means we don't need a module loader for our hello world which simplifies things a lot.

Module loaders are cool, and we will get to them, but not in a hello world.

app.js

Our code.

What is UMD?

UMD (as in angular-all.umd.js) stands for Universal Module Definition. It is a common specification for exposing variables. In this case, it creates the ng global for us.

We don't have to use it. We can explicitly import Angular components wherever they are required. More on this later.

HTML5 Skeleton

An HTML skeleton includes the necessary JavaScripts, then declared an app component.

Here's a skeleton HTML page for you to copy.

<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/2.0.0-beta.3/angular2-polyfills.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/2.0.0-beta.3/Rx.umd.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/2.0.0-beta.3/angular2-all.umd.js"></script>
<script src='app/app.js'></script>
</head>
<body>
<app>Hello from the HTML</app>
</body>
</html>

What is that <app> tag?

The <app> tag is going to be a component. We will create a component that matches that tag, and add behaviour and a template to it.

The JavaScript

Now we need to create our app component. In app.js, include the following:

// First define the component. I'm just whopping it in a global variable here.
// Globals are uncool, but this is fine for our simple hello world.
var AppComponent = ng.core
.Component({
selector: 'app', // Look for the <app> element in the HTML
template: '<p>Hello World!</p>' // Insert this template
})
.Class({
constructor: function() {} // An empty constructor
});

Now we have a component to match the <app> tag

Bootstrapping

In Angular 1 we could bootstrap an application automatically using the ng-app directive. In Angular 2 we must manually bootstrap our app.

We wait till the DOM is loaded, then we call ng.platform.browser.bootstrap to initialise the application.

// Wait till the DOM is loaded, them Bootstrap the component.
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(AppComponent);
});

You can see this in action here:

http://codepen.io/superluminary/pen/ZQMNqa?editors=1010

Exercise - Mucking about with code

As coders we learn best by getting our hands dirty, by tinkering. Let's get tinkering.

Fork this pen http://codepen.io/superluminary/pen/ZQMNqa?editors=1010 (click the fork button at the top).

  1. Now improve on this template a little. Change the p into an h1.
  2. Make it say goodbye world.
  3. Change the html selector from app to monsier-moreau. Update the HTML as well. Make it work.

Exercise - Looking at errors

Angular 1 would often fail silently on error. Angular 2 is great at error handling. Let's make some mistakes.

Codepen has a console button at the bottom. Click it to see the errors. Scroll down until you see something meaningful.

  1. Break the template. Change it to this: <p>Hello World!</>
  2. Break the selector. Change it to 'abc123', but don't update the html.

Angular 2 is much more strict. It will not try to make everything right for you, and will fail loudly and explicitly, which is generally what we developers like.

Exercise - Make your own

The goal of this exercise is just to get something working for real.

Break out of the pen. Make your own variation of the hello world app in an editor. Use the HTML skeleton above.

Save it in a folder and view it in a browser.

You don't need to have a server running or anything special, just open the HTML file in the browser as a file.

If you run into errors, open the console, view them and fix them.

Exercise - Visual Studio

If you need to develop in Visual Studio, you may have a bit of a culture shock when you start using Angular. We use static HTML templates and compile in the browser using JavaScript. The role of the server is dramatically reduced.

Visual Studio 2015 has excellent support for Angular. MS TypeScript is the language of Angular 2, and VS 2015 has Gulp and Node built right into it.

You may however have to adjust your thinking just a little bit, and you will have rather more hoops to jump through.

Creating the Project

  1. First create a new project.
  2. From Templates, create an ASP.Net Web Application.
  3. Choose Empty to create a completely empty application. We won't be using any of the features of .Net in our front end application.

Create the HTML file

  1. Right click your new application, add new item, and create an html file. Call it index.html. This is our template.
  2. Insert a little bit of text inside it.
  3. Now right click the file and open in browser. See the text?
  4. Alt tab back to Visual Studio and make a change to the text.
  5. Now alt tab back to your web browser. Press refresh. See the change you made?

Getting Angular

We have 3 choices here.

  1. We can use NuGet to install Angular.
  2. We can simply download Angular into our Scripts folder and link to it.
  3. We can link to the Angular CDN, as show in the hello world example above.

If you use Nuget

At time of writing there was not a viable Nuget package for Angular 2. If you choose to use NuGet, make sure you get the UMD version, i.e. angular2-all.umd.js

  1. Right click the project in the solution explorer and choose manage NuGet packages.
  2. Choose Angular 2 Core from the list. It will be installed into your Scripts folder.

Linking Angular

Now we need to link Angular. Because this is the front end we do this with a script tag right in the html.

<script src="/Scripts/angular.js"></script>

Now Attempt the Hello World exercise.

Bound Variables

Any attributes of our component are available in the component template. These are plain old attributes of the component.

Bindings are live, so whenever an attribute of the component updates, the template will be updated to match.

Binding in Angular 1 vs. Angular 2

In Angular 1 we used a $scope object to pass variables to and from the front end. Anything we wrote to $scope would be available in the template. We could $watch $scope, and make things happen in response to changes. Bound variables were available in a $scope and any child $scopes.

In Angular 2, our components themselves hold our bind variables. We write to this in our constructor. Bound variables are only available to the current component, not child components.

We bind with curlies {{}}

We have a range of binding options in Angular 2, the simplest uses the curly brace syntax, like so:

<div>Hello {{dinosaurName}}</div>

Here's that in context:

var AppComponent = ng.core
.Component({
selector: 'app',
template: '<h1>Hello {{dinosaurName}}</h1>'
})
.Class({
constructor: function() {
this.dinosaurName = "Liopleurodon";
}
});
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(AppComponent);
});

http://codepen.io/superluminary/pen/GoYaYa?editors=1010

Including Expressions

We can include expressions in our curly braces, for example we can do maths or string concatenation.

<div>1 + 1 = {{1 + 1}}</div>
<div>{{"Hello " + "World"}}</div>

Exercise - seconds in a day

  • Create a micro app that simply outputs the number of seconds in a day.
  • Extend it to show you the number of weeks in a human lifetime of 80 years.

You can either generate these values in the component, or directly into the template.

Live binding

If we change the value of an attribute of the component in any asynchronous way we can think of, the template will also update.

This includes:

  • setTimeout
  • setInterval
  • Ajax load events

Here we change the value using setInterval. In this instance the template will update every 500ms with a new number.

This magic is achieved using a piece of kit called Zone.js which is integrated into Angular. If we open up setInterval in a console, we will find that it has been overridden with a custom setTimeout. This does the same thing, with the addition that it will fire an event that we can subscribe to.

var AppComponent = ng.core
.Component({
selector: 'app',
template: '<h1>{{count}}</h1>'
})
.Class({
constructor: function() {
var vm = this;
setInterval(function() {
vm.count ++;
}, 500);
this.count = 1;
}
});
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(AppComponent);
});

Exercise - A clock

Use setTimeout to build a little clock that shows the time of the day. You can make it as fancy as you like.

Create a clock component. In a timeout, set values for vm.hours, vm.minutes and vm.seconds.

Note that this can also be achieved using pipes. More on this later.

Calling Functions - Side effects

We can also call functions in a binding, but if we do this we need to be careful. Angular 2's change detection works in a single pass. If you call a function from the template that changes a value that is referred to elsewhere, you will end up with an inconsistent result, and Angular will throw an error.

Further Reading

Dealing with Browser Events

Angular 2 has a new special syntax for dealing with events. In Angular 1 we had many special case directives to handle user interactions, such as ng-click, ng-submit, etc.

In Angular 2, events are no longer handed by special directives. There is a generic syntax. This means that we can handle any type of DOM event, and also custom events that our components might emit.

We handle a click event like this:

<a (click)="handleClick()">
Click Me!
</a>

If you don't like this syntax, we have a more traditional XML syntax available like so:

<a on-click="handleClick()">
Click Me!
</a>

We then need to define a our handleClick function inside the class decorator, like so:

.Class({
constructor: function() {
this.handleClick = function() {
console.log('clicked');
}
}
})

Put it all together:

var AppComponent = ng.core
.Component({
selector: "app",
template:
`
<a (click)="handleClick()">
Click Me!
</a>
`
})
.Class({
constructor: function() {
this.handleClick = function() {
console.log('clicked');
}
}
})

Dealing with events in the expression

We don't need to shell out to a handler to deal with events, although it's often a good idea. We can write an expression to deal with them right in the template, like so:

var AppComponent = ng.core
.Component({
selector: "app",
template:
`
<a (click)="name = 'Dave'">
My Name is Dave
</a>
{{name}}
`
})
.Class({
constructor: function() {}
})

Exercise - A Click Counter

Make a little click counter. Each time you click a link, the counter should increment.

For bonus points, make a down button as well, so you can change a value with up and down clicks.

*Note that i++ or i+=1 is not currently supported in Angular expressions. You will need to use the longhand i = i + 1;

Getting the Event

We can get a reference to the event by passing $event from the template. This is a real DOM event, complete with all the information you would expect.

<a (click)="handleClick($event)">
Hello World!
</a>

We then handle this in our handler:

this.handleClick = function(event) {
console.log(event);
}

Exercise - Mouse Coordinates

We are going to access the mouse coordinates in a div. Use the following scaffold:

var AppComponent = ng.core
.Component({
selector: "app",
template:
`
<div style="width:400px; height:400px; border:1px solid black"></div>
`
})
.Class({
constructor: function() {}
})

When the user clicks on the div, grab the mouse coordinates from the event and output them on the page.

For bonus points, make this happen when on mousemove

Nesting Components

Angular 2 is built out of components. Up till now, our applications have consisted of a single component, but a real app will be made out of a tree of components, one inside the next.

Because child components are injected into their parent, order of definition matters. We can get around this using a module loader (more on this later).

In this instance, because we have no module loader, we must define the child component first.

A Cat Component

Let's create a component that can sit on our page. I'm going to make a Cat Component to render out a single cat.

var CatComponent = ng.core
.Component({
selector: 'cat',
template: '<h1>Here is {{catName}} the cat!</h1>'
})
.Class({
constructor: function() {
var vm = this;
vm.catName = "Fluffy"
}
});

Now for the app

Next we define the parent component, which will be the root of the tree:

var AppComponent = ng.core
.Component({
selector: 'app',
template: '<cat>hello</cat>',
directives: [CatComponent]
})
.Class({
constructor: function() {}
});

Finally we can bootstrap

document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(AppComponent);
});

Exercise - A little web app

We are going to create a small web page using components that will render information about a single user / product / cat.

The sole purpose of this app will be to know about a user, and to render that user in a template.

First create an app template. Now create a header component and a footer component. Pop them in. Finally create a user display component that will display the user.

Now declare the user as an attribute of the user component. Use data binding to render the user data out.

Later we will look at passing data into our component and sending it back out again.

Angular brings back the DOM

For a long time we thought of a web page as a triune structure, composed of HTML for content, CSS for styling and JavaScript for behaviour.

If we wanted to modify our page, we would write JavaScript to modify attributes on the HTML. If we wanted style, we created style attributes. If we wanted to set the value of an input, we would set the value attribute. If we wanted text content, we would append it.

We forgot about the DOM.

The DOM lurks beneath, like an awesome Kraken, waiting to surface and eat us all

The DOM is the browser's representation of the web page. HTML initialises it, but once created it has a life of its own.

It is a tree of objects (Elements). Each Element has attributes, which are taken directly from the HTML, and properties, which are not.

Attributes look like this:

<img src="kitty_cheese.gif" />

The attribute here is src. Attributes are visible in the HTML, and also stored in the DOM as Element attributes.

DOM properties != HTML attributes

DOM properties are not visible in the HTML. They may be kept in sync with the HTML attributes, or they may not, depending on the exact behaviour of the attribute.

There are many more DOM properties than there are attributes. If we can set properties, we can do many more things than we can do if we just manipulate HTML.

Angular works directly on DOM properties.

Angular takes the DOM and makes it available directly in the HTML. Angular does not work on attributes at all. If you open your elements tab in Chrome and look at an Angular component, you may be surprised to see fewer attributes than you expected, perhaps none at all.

Setting DOM properties

We can create a template like this:

<img [src]="wiggling_hamster_bums.gif" />

Here we set the src property directly on the DOM, bypassing the attribute altogether.

Setting Attributes

The DOM is not perfect. It is wonky in many places and in many ways.

One issue you may run into when you start using Angular templates is the Can't bind to 'x' since it isn't a known native property error.

This happens because some attributes are not mapped to properties. This is a shortcoming in the DOM API. It's an omission that may be corrected at some future date.

an example of this would be colspan. You might see this error:

Can't bind to 'colspan' since it isn't a known native property

We can set attributes directly in out html by binding to attr.colspan:

<td [attr.colspan]="12" />

The Hidden Property

We can show or hide elements by setting the hidden property on an element.

<a on-click="iAmHidden=true" />
<div [hidden]="iAmHidden">Hey!</div>

Use the above as a base to create a little "read more link". When you click it, it flips the value of a boolean (check out the section on events if you can't remember how) and sets the [hidden] attribute of a div.

Extension

When you click it again, it becomes a read less link, so you can hide the content again.

Exercise - Tabs

In this section we are going to create a tabbed pane. This will have a row of links at the top, and a set of panels below. Clicking the links at the top will toggle visibility on the panels.

The HTML will look something like this:

Tab 1 Tab 2 Tab 3

Panel 1
Panel 2
Panel 3

When you click on Tab 1, Panel 1 is displayed, and Panels 2 and 3 are hidden.

Start out by adding click handlers to the tabs. Something like this: on-click="currentTab=1"

Now bind the hidden attribute to the panels, something like this: [hidden]="currentTab!=1"

Extension

Extract this into a component and integrate it with your user app (with the header and footer) from the previous section.

Coming up

Later we will see how we can extract the tabs themselves into a child component. We will also see another way of doing this using routing.

The Disabled property

We can disable inputs and buttons by setting the [disabled] property on the Element, like so.

<input [disabled]="true" />

Exercise - Disabled Submit Button

Create a little email signup form with fields for name and email, and submit button. Make it so that the button is disabled until you detect a keyup event in the name field and the email field.

Note, we will find a better way to do this when we get to the section on forms.

Extension

Integrate this form into your tabbed pane from the previous exercise.

Exercise - Hyper bonus exercise

Open up the DOM inspector in Chrome, right click an element, and choose 'inspect'. On the right, you will see a properties tab. This shows all the DOM properties you can modify. (Note that properties and attributes are mixed up in this tab). Have a play with some of these properties and see what you can do.

Making Angular 2 Components Receive Values (inputs)

Components are analogous to DOM nodes. Just as the behaviour of a DOM node can be affected by setting attributes on it, our components can also receive attributes (and property bindings) as inputs.

Attributes are the inputs of our components.

We tell our app it can receive inputs by setting an inputs attribute in the Component decorator, like so:

inputs: ["profile"]

Here's that in context:

var UserComponent = ng.core
.Component({
selector: "user",
inputs: ["profile"],
template:
`
<div>
user: {{profile}}
</div>
`
})
.Class({
constructor: function() {}
})

Passing the inputs in

We can pass inputs in several ways:

We can pass an attribute (which will come through as a literal)

<user profile="davey"></user>

We can pass a property from the parent component, which will be evaluated in it's current context:

<user [profile]="userProfile"></user>

If we want a string literal here, we can compose one in the expression:

<user [profile]="'mikey'"></user>

Or we could hardcode an object:

<user [profile]="{name: 'mikey'}"></user>

Finally we might use a curly expression to bind an expression to an attribute. Though this works, and may feel quite attractive, it is not the preferred way to do this. Changing an attribute may trigger a browser reflow, where the browser re-renders the page. This is an expensive thing and may slow down your app.

<user profile="{{name}}"></user>

Here's the above in context

var AppComponent = ng.core
.Component({
selector: "app",
directives: [UserComponent],
template:
`
<user [profile]="name"></user>
<user [profile]="'mikey'"></user>
<user profile="davey"></user>
<user profile="{{name}}"></user>
`
})
.Class({
constructor: function() {
var vm = this;
vm.name = 'stewey'
}
})
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(AppComponent, [])
});

Exercise - Bind the profile component

Remember the profile component we made before that displays a single user or cat? We are going to parameterise that, so that the user can be passes in from outside.

Add an inputs attribute to your component decorator. Now make the user component receive a parameter that tells it which user to show.

Bonus points

Create several user components, and add them to your tabbed pane, so that you can tab around and see multiple users.

Event Emitters (Outputs)

Just as an anchor can emit a click event, and a form can emit a submit event, our custom components can emit events that we make up too.

For example, say we have a likes button. We might have it output a 'like' event and an 'unlike' event. It we have a rating star component, we might have it emit a 'rate' event. We might extend that event object to include the number of stars.

We need to do 3 things to make our component emit an event:

  1. Configure our component to emit the event.
  2. Create the event emitter in our component.
  3. Tell the event emitter to emit the event.

Let's do this now. We are going to create a like button. We click it and it emits a 'like' event.

1. Configure the component

First we need to add the 'liked' to our component decorator:

.Component({
selector: "like",
events: ["liked"],
template:
`
<a (click)="handleClick()">
Like?
</a>
`
})

This tells Angular that this component will emit a liked event. Notice also that the template is calling a function called handleClick() when we click it.

2. Create the EventEmitter

Next we need to create an attribute of our component object to manage the event. We do this in our class decorator:

.Class({
constructor: function() {
var vm = this;
vm.liked = new ng.core.EventEmitter();
}
})

This will be an instance of ng.core.EventEmitter.

Emit the event

Finally we need to tell our class to emit the event. Let's extend our Class to handle a click event:

.Class({
constructor: function() {
var vm = this;
vm.liked = new ng.core.EventEmitter();
vm.handleClick = => {
console.log('clicked');
vm.liked.next();
}
}
})

Putting it together

Here's the whole component:

var LikeButtonComponent = ng.core
.Component({
selector: "like",
events: ["liked"],
template:
`
<a (click)="handleClick()">
Like?
</a>
`
})
.Class({
constructor: function() {
var vm = this;
vm.liked = new ng.core.EventEmitter();
vm.handleClick = function() {
console.log('clicked');
vm.liked.next();
}
}
});

Make use of the component

We can now make use of this component in a parent component, like so:

var AppComponent = ng.core
.Component({
selector: "app",
directives: [LikeButtonComponent],
template:
`
<like (liked)="handleLike()">Hello</like>
{{message}}
`
})
.Class({
constructor: function() {
var vm = this;
this.handleLike = function() {
this.message = "Thanks for liking us"
}
}
});

Notice the like component is now able to emit a liked event, which we can handle.

Exercise - like counter

Extend the code above so that we can count the number of likes. When the like counter emits a like event, have the counter in the parent template increase by one.

A user can like as many times as they want to.

Harder Exercise - Upvoting users

Change the above into an upvote/downvote component. Upvoting emits an upvote event. Downvoting emits a downvote event.

Now add this to your User component. Bind to the upvote in the user, such that when we click upvote, the user's voteCount property is increased. Vice-versa for downvotes.

Appending data to events

We can return data from our EventEmitter, just by passing it as a parameter. We might like to return a JSON object:

vm.liked.next({
name: 'davey',
age: 47
});

we can return an attribute of the component, or some made up data, even data that has been passed in from outside. Here's that in context:

var CatComponent = ng.core
.Component({
selector: "like",
events: ["liked"],
template:
`
<a (click)="handleClick()">
Like?
</a>
`
})
.Class({
constructor: function() {
var vm = this;
vm.cat = {
name: 'davey',
age: 47
}
vm.liked = new ng.core.EventEmitter();
vm.handleClick = function() {
vm.liked.next(vm.cat);
}
}
});

Integration Exercise - Select a user

Add a select button to the user component. Make it so that when I click the select button, an event fires and returns the user. Save this user on the parent component and display it nicely.

Bonus points

Create a component that displays the currently selected user. This will need to receive the user from the parent component as a property binding.

Styling Components with the Shadow DOM

First up, the Shadow DOM is not real - yet. Unless you want to restrict yourself to Chrome and Android Browser, we can't use it at time of writing. However, Angular does a reasonable job of polyfilling some of the features to give us a taste of what it might be like.

The Shadow DOM is a proposed future browser feature that will allow us to create separate regions of a webpage that work like separate documents embedded in the main document. It's being pushed by Google, but has precisely zero traction in other browsers at time of writing

This is an attempt to make HTML pages more modular. You might insert a widget into a page, and expect it to come with it's own styles that don't apply anywhere else.

It is rather like an iFrames, except the HTML is embedded in the document.

Support is limited

The Shadow DOM is currently only supported by Chrome and Android Browser, and may never gain wide adoption. See the current support tables here:

http://caniuse.com/#feat=shadowdom

Chrome uses the shadow DOM to style the insides of form elements for example. Angular mocks some of the features of the shadow DOM, so that we can use them today.

Adding styles in Angular

We add styles to our component by passing a styles attribute to our component's constructor or decorator.

var AppComponent = ng.core
.Component({
selector: "app",
template:
`
<div>hello from the shadow DOM</div>
`,
styles: [`
div {
background:red;
}
`]
})
.Class({
constructor: function() {}
})
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(AppComponent, [])
});

This generates namespaced styles

If we look inside the head section of our generated DOM, we see a new style tag like this:

<style>
div[_ngcontent-frc-1] {
background:red;
}
</style>

The key thing is this: [_ngcontent-frc-1]. This is a CSS2 attribute selector. It will select only elements that have an _ngcontent-frc-1 attribute. If we look inside our DOM at the component, we'll find a matching attribute has been appended to our template:

<app _nghost-frc-1>
<div _ngcontent-frc-1>hello from the shadow DOM</div>
</app>

Angular is using random generated attributes to namespace portions of the DOM, so the styles only apply to those components. This gives us much of the functionality of the shadow DOM in non-supporting browsers.

Exercise - Shadow Dom

In this exercise, we are are going to add styling to this little user tab bar component:

ng.core.Component({
...
template:
`
<ul>
<li>
<a on-click="tab='history'">history</a>
</li>
<li>
<a on-click="tab='info'">info</a>
</li>
<li>
<a on-click="tab='edit'">edit</a>
</li>
</ul>
`
...
});

Your styles might look something like this:

ul, li {
margin:0;
padding:0;
list-style:none;
}
li {
float:left;
}
a {
display:block;
padding:10px 20px;
background:#f88;
border-radius:10px 10px 0 0;
}

Extension, custom styles

We are going to extend our tab pane, so that when we click on a tab, that tab will become highlighted. We will do this by adding a class to the selected tab.

You can add a class to an element by binding to the class property, like so:

[class.selected]="tab=='edit'">

This will add a class of selected if the expression evaluates to true.

Add in the class binding, and apply some styling like this:

.selected a {
background:#f00;
color:%fff;
}

Now you should have a tab that changes colour when it is selected.

Hard Extension

Split your tab bar into 3 tab button components. These should be parameterised with a name, and should emit 'tab' events when they are clicked.

Make it so that your tab bar emits a tabSelected event when one of the tabs is clicked. Now add three divs beneath the pane that are hidden until the corresponding tab is clicked.

Disabling or going native

If we are only targeting Chrome or Android: perhaps we are building an mobile app for example, we can turn on full native shadow DOM support.

We do this by adding an encapsulation attribute: encapsulation: ng.core.ViewEncapsulation.Native. This is just a constant. It has the value 1.

It looks like this:

var AppComponent = ng.core
.Component({
selector: "app",
template:
`
<div>hello from the shadow DOM</div>
`,
styles: [`
div {
background:red;
}
`]
})
.Class({
constructor: function() {}
})

Now our DOM looks like this:

<app>
#shadow-root
<style>
div {
background:green;
}
</style>
<div>hello from the shadow DOM</div>
/#shadow-root
</app>

That #shadow-root specifies a new shadow DOM. You can see the styles are nested inside that shadow root.

Exercise - Enable the shadow DOM

Turn on the shadow DOM for your tab bar application. In Chrome, use the inspector to inspect your component. Notice how it is encapsulated with it's own style attributes.

Use CSS to apply a style to content the page. Perhaps make the text red with color:red. Notice how the page style doesn't affect the shadow DOM.

Inline Templates and ngIf

Sometimes we want to specify a template right inside another component. This is useful for things like:

  • conditional content
  • repeated content

These templates can be passed to directives, which can then render them in a variety of ways.

Directives?

A directive has a slightly different meaning in Angular 2 to Angular 1. In Angular 2, a directive can be thought of as a compiler directive. It extends the DOM compiler, giving you more options.

In Angular 2 a directive is just a component without a template. It operates on the current Element or Component, changing its behaviour, adding listeners, DOM properties, etc.

Later, we will build a directive of our own.

Making a template

We create a template using the <template> tag. If we append a <template> tag to a component template, it will not be output and all the content in it will be ignored.

ngIf

To make this template do something, we pass it to a directive such as ngIf.

This directive will completely remove an element from the DOM if an expression is falsey, or append a clone if it is truthy. We set a property on the template to activate the directive:

<a on-click="visible = !visible">Click</a>
<template [ngIf]="visible">This is the template</template>

We also have a slightly strange, albeit convenient shorthand. We can write this instead:

<div *ngIf="visible">This is the template</div>

The entire div will become the template and will be injected into the ngIf directive.

Exercise - Payment widget

We are going to create a little payment widget, like this:

<form>
<select>
<option value='visa'>Visa</option>
<option value='mastercard'>Mastercard</option>
</select>
<div>
<label>Start Date:</label><input type='date' />
</div>
<div>
<label>End Date:</label><input type='date' />
</div>
<div>
<label>CVC:</label><input type='number' />
</div>
<button>Purchase the Cheese</button>
</form>

The user can choose Visa or Mastercard from a dropdown.

If they select Mastercard, show the start date, end date fields. If they choose Visa, show the CVC field.

Harder Extension - automatically selecting Visa or Mastercard

Refer to this SO answer: http://stackoverflow.com/a/72801/687677

Write a small function that Parses the first digits of the card number to automatically select Visa or Mastercard. Bind [value] on the select.

Repeat and Filter

NgFor is a directive that allows you to show a list of things. Say we have an array of cheese declared in out component:

vm.cheeses = ['manchego', 'dolchelatte', 'fingers']

We can render it like so:

<ul>
<li *ngFor='var cheese of cheeses'>
{{ cheese }}
</li>
</ul>

Template variable

We have a shorthand syntax for the template variable. We can also write:

The * signals that the li should be used as a template. var cheese of cheeses signals that we should create a template variable a value that exists only in the context of the template. This will contain each of the cheeses in order.

<ul>
<li *ngFor='#cheese of cheeses'>
{{ cheese }}
</li>
</ul>

Exercise - Generate Tabs from an Array

We are going to use an array to generate the tabs for our tab component.

First of all, generate an array containing the tabs that you would like to display. Add it to VM.

Now pass that array in as a property to the tabs component.

Use an ngFor to iterate over the array and output each of the tabs in turn.

Custom Pipes

We have used pipes before for transforming data. For example, we have uses pipes to convert text into upper case.

A pipe is simply an object that has a transform function. We can write our own pipes very easily. This is a simple pipe that receives a value, and prepends it with the word "toast"

var ToastPipe = ng.core
.Pipe({
name:'toast'
})
.Class({
constructor: function() {},
transform: function(value) {
return "Toast " + value
}
})

We would make use of it like this:

var AppComponent = ng.core
.Component({
selector: "app",
pipes: [ToastPipe],
template:
`
{{"hello" + "world" | toast}}
`
})
.Class({
constructor: function() {}
})

Notice that we have to tell our template we want to use it by passing a pipes attribute.

Exercise - Reversing a String

We can reverse a string by using code a little like this:

"abc".split('').reverse().join('');

Write a pipe that will reverse an string, so I can say hello | reverse and it will output olleh

Harder Exercise - Sort Filter

I have an array of people like this:

[
{
name: "Hairy Margo",
age: 43
},
{
name: "Snake-eye Sammy",
age: 9
},
{
name: "Manchego McGraw",
age: 68
}
]

I would like to be able to sort these people by name.

Write a filter that accepts an array like this and sorts it by the name attribute. You may wish to read up on the array sort method on MDN

Use ngFor to output the sorted filter.

Extra difficult extension

Parameterise the filter, so you can choose which attribute to sort on.

Now add buttons to the page, so that you can click them to sort on different attributes.

Building Forms in Angular 2

In the previous sections we looked at input and output with our Angular 2 components

  • Input: via attributes and properties
  • Output: via events which we can listen to

This means we can now interact with forms.

An <input /> DOM node will emit many different events when our user interacts with it, including:

  • keyup - when the user presses a key
  • focus - When the user enters the field
  • blur - When the user leaves the field
  • input - When the value property changes

We can bind to any of these and have our update events fire when the event is triggered.

The input event

The most interesting of these is the input event. This fires whenever the value attribute changes.

<input on-input="onInput($event)" />

We can deal with this event like so:

.Class({
constructor: function() {
var vm = this;
vm.onInput = function(evt) {
this.catName = evt.target.value;
}
// this.username = new ng.common.Control('username')
}
});

Initialising the value:

We might want to initialise the value of catName. We can do this in our template using simple property binding, like so:

<input [value]="catName" on-input="onInput($event)" />

We might then output this value somewhere using curlies:

{{catName}}

Exercise - Two way binding

Create a little UserEdit component now. Put a form in it that will allow us to edit a user.

Bonus Points

For bonus points, allow the user to be passed in from the parent template.

NgModel

NgModel is a directive. We haven't seen any directives yet. A directive is just a component which doesn't have it's own template. Instead it modifies the template of the component or element it is applied to.

We will build a directive of our own in a later exercise.

The NgModel directive creates all of this data binding for us. It will bind a value and add an input listener.

<input [(ngModel)]='catName' />

Notice the [()] brace combo. This shows that we are binding a property, to the return value of the event listener. It's a two way binding.

Exercise - ngModel

Re-implement the above using 2 way binding.

Binding to submit

We can bind to the (submit) event of a form to have something happen when we submit. This is better than binding to the (click) event of the button, because there are ways to submit a form other than clicking the button, for example the user might press enter on an input element.

<form (submit)="onSubmit()">
<button>Go!</button>
</form>

If we have used ngInput to bind our inputs, we can then use this data to update a model.

Hard Exercise - Update the user

Extend your user userEditor component, such that the user is only updated when we click submit.

Further Reading

Dependency Injection with Angular 2 and ES6

In this section we will look at what Dependency Injection is, how it worked in Angular 1, and how it works now in Angular 2.

DI is a pattern

Dependency injection is a pattern in which a components dependencies are passed in from the outside by the framework.

If a toaster needs bread, rather than constructing its own bread, the person requiring toast inserts the bread.

Pretend Code

Let's invent some code. Here's a toaster:

var Toaster = function(bread) {
this.toast = makeToastOutOf( bread )
};

And here's a pretend dependency injector that will take care of making a toaster and filling it with bread:

var magicalInjector = new MagicalInjector()
magicalInjector.knowsHowToMakeA( Toaster ).with( Bread )
var myToaster = magicalInjector.pleaseGiveMeA( Toaster );

Our pretend magicalInjector knows the toaster needs bread. It creates bread and uses it to initialise the toaster. The Bread may itself have an injector.

A more practical example might be an API accessor. We isolate the API code into an object, then inject it where it is needed.

This isolates our components one from another, and allows us to substitute mock components for real ones for testing.

DI in Angular 1 (how it used to be)

In Angular 1, dependency injection was magical. Every component had a unique string name. You simply wrote a function that would receive parameters, named them appropriately, and Angular would automatically inject the correct components from it's registry of 'Angular magic stuff'.

We might simply write:

angular.module('app', [])
.controller('appController', function(pageService){});

Angular would automagically take care of instantiating pageService and passing it to the appController.

This was done by using toString to convert the function body into a string, then using a regular expression to determine the names of the components, which could then be pulled from Angular's list of 'stuff'. More on this here.

DI in Angular 2

Angular now uses objects for tokens rather than strings.

This means any object is potentially injectable. If the object you inject is newable, Angular will take care of ensuring it is a singleton.

Create the injectable

Let's start out by creating a service. This is just a function that returns an object. I've just made this as a global here. I'm not using any requirejs or systemjs module magic.

var CheeseService = function() {
this.get = function() {
return ['Manchego', 'Camberzola', 'Stinky Bishop'];
}
}

Notice there's nothing special about this at all. It's just a factory function. The injector is going to call this function to get the object.

Create a component to receive the injectable

Now lets make an app component. This is just a regular component. We would like it to have access to the InjectedService instance

var CheeseComponent = ng.core
.Component({
selector: "cat",
template:
`
<div *ngFor="#cheese of cheeses">
{{cheese}}
</div>
`
})
.Class({
constructor: [CheeseService, function(cheese) {
// console.log(service)
this.cheeses = cheese.get();
}]
});

Notice that the constructor now contains an array. We choose what we would like to inject, and the Angular takes care of business.

Finally we bootstrap the app. When we bootstrap, we can declare the components that our app will depend on. This sets up the global injector. We will see shortly how to set up child injectors.

document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(AppComponent, [InjectedService]);
});

We see that any object we can make can be an injectable, not just the core Angular components. This gives us freedom, at the expense of some verbosity.

Exercise - User Service

Create a service that has a method that will return a list of users. It should have a get method that returns those users. This is our data source.

Now create a userListComponent that will inject this service, and render the list of users.

Further Reading

AJAX

AJAX is a technology that allows us to talk to a server directly from JavaScript without reloading the page.

JSONP

JSONP is a hack that allows us to make cross domain requests to a server that doesn't support CORS. It works by injecting a script tag into the page that has the same URL as the site we're trying to access.

To use it, First we must configure our injector to find the JSONP_PROVIDERS. This is a list of injectable dependencies that will let us do JSONP.

document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(AppComponent, [ng.http.JSONP_PROVIDERS])
});

Now we can inject it into our constructor, like so:

.Class({
constructor: [ng.http.Jsonp, function(jsonp) {
var vm = this;
vm.cats = [];
var url = "http://my-server.com/cats.json?callback=JSONP_CALLBACK"
jsonp.get(url)
.toPromise()
.then(function(response) {
console.log(response.json());
vm.cats = response.json()
})
}]
})

Notice the JSONP_CALLBACK in the url. This is a magic parameter. Angular will do a find and replace for this value in the string, and will add an actual callback fuction which it wil maintain.

Exercise - Flickr

We are going to use the Flickr API to pull a list of images about a tag.

The flickr JSONP endpoint looks like this:

http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=JSONP_CALLBACK

  1. First inject the ng.http.JSONP_PROVIDERS into your global injector. Do this when you bootstrap. See above for how.
  2. Now inject ng.http.Jsonp into your constructor.
  3. In the constructor, make your call, like this: jsonp.get(url)
  4. Store the result in the component. vm.feed = response.json() See above if you don't know where to put this line.
  5. Now in your template, put a line like this: {{feed}}. This will output the JSON feed onto the page.

Extension - List the cats

The JSON feed that comes from the server contains an items attribute, which is an array. Use ngFor to loop over the array

Harder Extension - Allow the user to search for a tag.

The URL has a tag parameter embedded in it. Create a search field with a button. When the user clicks search, trigger a JSONP request that will perform the search.

Harder Exercise - Extract the AJAX into a service

Having AJAX in our component is not so pretty. Better to extract it into a service. We can create a service that will receive injectables using ng.core.Class, like so:

ng.core.Class({
constructor: [ng.http.Jsonp, function(jsonp) {
this.getFeed = function() {
// Do your work here
}
}]
})

Build a class like this, and put your call into it. Have it make the AJAX call for you, and return a Promise. In your component you can call getFeed, and have it return the promise, which you can then chain onto.

Core JavaScript Topics - Understanding Closure

Closure can be a tricky concept to get one's head around. It works like this:

  1. Functions are objects and can be declared anywhere.
  2. If I declare a function within another function, the inner function has access to the local variables of the outer function.
  3. Normally when a function exits, all it's local variables are destroyed, but…
  4. If I hang on to the inner function in some way, the local variables of the outer function are still needed by the inner function.
  5. They are still in scope, so they are not garbage collected.

This is closure in a nutshell. Here's an example:

var outer = function() {
var a = "A Local variable"
var inner = function() {
alert(a)
}
window.fnc = inner
}
outer();
fnc();

When we call outer, we define inner and assign it as an attribute of window (in other words, a global variable) inner persists after outer has exited, so the local variable a is still in scope, and still accessible to inner.

Exercise - Try it out

  1. Enter the above code and verify it works.

Self Executing Functions

It seems a bit superfluous to have to call outer to create inner. Outer only exists here to define inner, there's no need for it once inner has been made.

If only there was a way to avoid referencing outer. Thankfully there is.

Here we have a self executing function. The function is in braces, and there are braces at the end. We are declaring an unnamed function, then running it straight away, dispensing with the need for outer.

(function() {
var a = "A Local variable"
var inner = function() {
alert(a)
}
window.fnc = inner
})()
fnc();

Exercise - Guess the output

For each of the following, try to work out what the output will be. Run the code to check your answer.

1.

var a = 12;
(function() {
alert(a);
})();

2.

var a = 5;
(function() {
var a = 12;
alert(a);
})();

3.

var a = 10;
var x = (function() {
var a = 12;
return (function() {
alert(a);
});
})();
x();

4.

var a = 10;
var x = (function() {
var y = function() {
var a = 12;
};
return function() {
alert(a);
}
})();
x();

5.

var a = 10;
var x = (function() {
(function() {
a = 12; // <<< look carefully at this line.
})();
return (function() {
alert(a);
});
})();
x();

6.

var a = 10;
(function() {
var a = 15;
window.x = function() {
alert(a);
}
})();
x();

Further Reading

Read my post on "What is a closure" on Stack Overflow

Core JavaScript Topics - Welcome to JSON

There are several different syntaxes for declaring objects in JavaScript. Of these, the cleanest and most commonly used is JavaScript Object Notation (JSON), pronounced Jason.

Packaging our code up into JSON gives us several significant advantages:

  1. It looks pretty.
  2. It reduces the number of objects in the global namespace.
  3. It's great for AJAX, smaller than XML and readily parsable both client and server side.

In this series of exercises we'll write some JSON and create a little API.

JSON looks like this:

var mario = {
description:"Small and jumpy. Likes princesses.",
height: 10,
weight: 3,
speed: 12
}
var bowser = {
description: "Big and green, Hates princesses.",
height: 16,
weight: 6,
speed: 4
}

Here we have defined two objects, one called mario and on called bowser. As you can see, they consist of a series of name value pairs. We can access the values like so:

alert(mario.description);

We can modify values like so:

mario.description = "Big and smashy, having eaten a mushroom"

We can even add new attributes like so:

mario.can_fly = true;

Objects are useful for keeping things neat and tidy. We have encapsulated all the information about Mario in a single place so it's easy to get at and understand.

Exercise - canFly

  1. Enter the above code
  2. Alert the values of mario.description and bowser.description
  3. Use your web inspector to add a breakpoint. Check the values of the JSON objects in the debugger.
  4. Add a canFly attribute to Mario using the dot syntax. Again, check the debugger.
  5. Create an entry for Luigi. He's a bit thinner that Mario, but slightly taller, and he wears a green outfit.

A Rather Dull Mario Game

If we wanted we could write a little Mario game using these objects. Lets create a little function to see who wins in a boss battle:

var boss_battle = function() {
if (mario.speed > bowser.speed) {
alert("Mario has escaped and saved Peach");
} else {
alert("Bowser has stomped all over Mario. Mario is dead.");
}
}
boss_battle();

As we can see, Mario wins. Let's swing the odds a little in Bowser's favour. Bowser can go into a powered up mode where he swoops from side to side on the screen. We'll call this Bowser Boost.

Let's extend our game a little to add Bowser Boost:

var mario = {
description:"Small and jumpy. Likes princesses.",
height: 10,
weight:3,
speed:12
}
var bowser = {
description:"Big and green, Hates princesses.",
height: 16,
weight: 6,
speed: 4,
boost: 0
}
var boss_battle = function() {
if (mario.speed > bowser.speed + bowser.boost) {
alert("Mario has escaped")
} else {
alert("Bowser has stomped all over Mario")
}
}
boss_battle();
bowser.boost = 20
boss_battle();

Exercise Invincible

  1. Enter the above code and get it to run. Who wins?
  2. Give Mario an invincibility attribute. If invincibility is true, Mario always wins. Unfair I know.

Don't worry, we are going somewhere with this. Next!

Lets clean this code up a little

  1. Add two more attributes to each character: attack_power and name.
  2. Extend the boss battle function so it receives two parameters, contestant_1 and contestant_2.
  3. Rewrite it so that instead of running the conditional, it simply prints out the name of the contestant with the highest attack_power. This renders all the other attributes irrelevant, it only checks the attack_power.

Functions within JSON

This is all very well. We have Mario and Bowser objects and a function to compare them, but it seems a little artificial having all our functions scattered all over the place like this. Also, the more functions we have in our global namespace, the more likely it is that they will conflict, and we'll accidentally overwrite something important.

Let's see if we can improve things.

var mario_world = {
mario: {
name:"Mario",
description:"Small and jumpy. Likes princesses.",
celebration: "Mario wins and does a little dance",
height: 10,
weight:3,
speed:12,
attack_power: function() {
return this.weight * this.speed;
}
},
bowser: {
name:"Bowser",
description:"Big and green, Hates princesses.",
celebration: "Bowser wins and does a big roar",
height: 16,
weight: 6,
speed: 4,
boost: 0,
attack_power: function() {
return this.weight * (this.speed + this.boost);
}
},
boss_battle: function(contestant_1, contestant_2) {
alert(contestant_1.name + " vs " + contestant_2.name);
if (contestant_1.attack_power() > contestant_2.attack_power()) {
alert(contestant_1.celebration);
} else {
alert(contestant_2.celebration);
}
}
}
mario_world.boss_battle(mario_world.mario, mario_world.bowser);

Ah, that's better. As you can see, this entire program is encapsulated within a single object called mario_world. The mario_world object is the only object in the global namespace. the boss_battle function is now an attribute of mario_world, and each contestant has an attack_power function that works out his strength in a battle. The boss_battle function is parameterised, so you can battle any contestant against any other.

We have also written an attack_power function which is implemented by bowser and mario. This calculates the attack power from a series of other parameters.

Because functions are objects, we can assign them as attributes of objects. They're key value pairs, but if you've programmed before, you'll notice they work like methods. We can now call methods on our JSON objects.

What is this?

You might have noticed also that in the attack_power function we use the "this" keyword. We say this.speed and this.boost.

This is a troublesome keyword that trips up even quite advanced JavaScript developers all the time. "this" refers to the context in which the code is currently operating in. In this case, the context is mario (or bowser) and so this refers to these variables. That's because the attack_power function is an attribute of mario. This points to the object the current object is a member of. Your knowledge of JavaScript will be measured by your understanding of scope, and the "this" keyword, so we'll come back to this, but for now, let's make a real game…

Exercise - Princess Peach

  1. Enter the above code and make it work.
  2. Add Princess Peach. The princess is smaller and lighter but compensates for this by being quicker and having a dash mode. Battle Peach vs Bowser and Peach vs Mario.
  3. Add an activate_boost function to Bowser that adds 5 to his boost. You should be able to call mario_world.bowser.activate_boost.
  4. Add a toggle_dash function to peach. Calling it should activate or deactivate her dash attribute.
  5. Just for kicks, change the boss battle function so instead of outputting alerts, it returns a string. Now we have an API.

Exercise - Bundle it into an Angular Service

Having defined our API, it's now time to expose it to Angular. Create a service that simply returns the API you created as a JSON object.

  1. Extend your HTML page. Create a little form. Add two dropdowns, containing the names of the available characters. Ideally you will populate these dropdowns by querying the API and using an ngFor. You may need to adjust your JSON to do this.
  2. Add a "Fight" button that will make those two characters battle.

Further Challenge Exercise

Now lets take things a little further. I'll leave this up to you.

  1. Add a button for Bowser Boost. When you click it, his boost is increased.
  2. Add a Peach Dash button. Every time you click it, Peach's dash mode is toggled on and off.
  3. Make it so that when the dash and boost buttons are only visible when Peach or Bowser are selected.
  4. Handle the case where the battle is a draw.
  5. Grab images for each character. Store the URL of the image in the JSON. Draw an arena and put the characters in it when they are selected.
  6. Destroy the losing character with an appropriate animation.
  7. When the fight button is clicked, animate a fight briefly before destroying the loser.

Downloads and sample answers

You can find sample answers in the Dropbox here.

Core JavaScript Topics - Object Orientation

You may have noticed JavaScript doesn't have classes. In fact, as we have seen with JSON JavaScript doesn't need any of that to make an object, but without classes we do lose a few pieces of functionality, specifically:

  • Constructor functions
  • Inheritance

In fact we don't lose any such functionality. Because there are no classes, we are free in JavaScript to use any object as the parent of another object.

We can also use any function as a constructor function.

Constructor functions

Constructor functions are just regular functions. By convention we capitalise them, like this:

var Toaster = function() {};
var CheeseDip = function() {};

We then create a new object using this function with the new keyword:

var myToaster = new Toaster();
var myCheeseDip = new CheeseDip();

In our Toaster function we can initialise the toaster by writing to the 'this' variable, which in the context of a constructor will be set to point to the object under construction's prototype.

var Toaster = function() {
this.goPing = function() {
alert('ping');
}
this.toastQuotient = 2;
};
var myToaster = new Toaster();
myToaster.goPing();

Exercise - Constructor Functions

Make me a little car (or choose your own domain). The car should have a latitude and longitude attributes. It should have goNorth, goSouth, goEast and goWest functions. It should have a toString method that prints the current location.

Now go for a drive.

Prototypical inheritance

Most classical languages have classes which inherit from one another. Objects are then instances of classes. JavaScript has no classes, so objects inherit directly from other objects, which when you think about it is quite sensible.

We talk about an object's prototype. The prototype is the object which our new object will inherit from.

Here's a cut down example, we're just inheriting from a humble JSON object called x.

var x = {y:'z'}
var A = function() {}
A.prototype = x;
a = new A();
a.y; // 'z'

Here's a more practical example. This time our Toaster inherits from an instance of Appliance.

Notice we inherit from an object, not from the constructor function.

var Appliance = function() {
this.goPop = function() {
alert('pop');
}
};
var Toaster = function() {
this.goPing = function() {
alert('ping');
}
this.toastQuotient = 2;
};
Toaster.prototype = new Appliance();
var myToaster = new Toaster();
myToaster.goPing();

Prototypical inheritance Exercise

Create a Vehicle constructor to be a parent of the Car. Move your functions and attributes up into the vehicle.

Now create a new Car, it should still be able to drive.

Add a new method to the car, so we can change the oil.

Now make a bike object which also inherits from Vehicle and instantiate it. Very nice.

Object.create

ES5 gave us a new Object prototype method designed to make inheritance easier.

var appliance = {
goPop: function() {
alert('pop');
}
};
var toaster = Object.create(appliance)
toaster.goPing = function() {
alert('ping');
}
toaster.goPop();
toaster.goPing();

Object.create lets us create a child of any other object. The prototype chain can be as long as you like.

Exercise - Object.create

Re-implement your car using object.create. Be sure to keep you old version around somewhere.

Composition

Because objects in JavaScript are just associative arrays, we can compose an object from parts of another object without using inheritance just by copying attributes from one to another.

var toaster = {
goPing: function() {
alert('ping');
}
};
var cheeseWhizMachine = {
goPing: toaster.goPing
}
cheeseWhizMachine.goPing()

We can use this to implement a decorator pattern:

var pingDecorator = {
decorate: function(obj) {
obj.goPing = function() {
alert('ping');
}
}
}
var cheeseWhizMachine = {};
pingDecorator.decorate(cheeseWhizMachine);
cheeseWhizMachine.goPing();

Composition Exercise

Create a navigation decorator that will decorate any object with goNorth, goSouth, etc methods.

Optional Extra Angular Exercise

If you finish first, create a driving simulator.

Treat your newable function as an Angular service.

There should be 4 buttons, up, down, left and right. Create a component that draws the car on the screen This should listen to the service and decide where to draw itself. You will need to bind to style.top and style.left.

Create button components (one component, 4 instances) Wire up the buttons with events.

Further Reading

The MDN developer docs have a good write up of all of this here: Inheritance and the prototype chain

This

This is a special variable in JavaScript. It is instantiated when you call a function,and it's value will change depending on how you call the function.

This in the global scope

If we open a console and simply type this, we get back "window".

console.log(this)
//Window

The default value for this is the global scope. If we do nothing else, this will be the global scope.

This within a function

Likewise, the value of this within a global function will also be the global scope:

var a = function() {
console.log(this);
}
a();
// Window

This in an object

If we are calling a function as an attribute of an object, this is set to that object. This is always the object we are operating inside of.

The simple heuristic is that this is the object immediately preceding the dot.

var car = {
speedUp: function() {
console.log(this);
}
}
car.speedUp();
// outputs the car
var speedUp = car.speedUp;
var speed = 0;
speedUp();
// outputs the global scope object Window
var bus = {
speedUp: car.speedUp
}
bus.speedUp();
// outputs the bus

This in a closure

People often confuse this. The value of this is related to JavaScript as an object oriented language. It is not affected by the closure.

var fnc = function() {
var inner_fnc = function() {
console.log(this)
}
inner_fnc();
}
fnc();
// Window

Setting this with apply

We can also manually set the value of this to any value we like. This trick is employed by several frameworks to make your life easier.

For example, jQuery will set the value of this to be the DOM node that received an event. Angular will set the value of this to the controller when you call a function on that controller's $scope object.

var myFunc = function() {
console.log(this);
}
var myObj = {
test:"cucumbers"
}
myFunc.apply(myObj,[]);
// outputs myObj

Exercises - Guess the Value of This

Question 1

var x = function() {
console.log(this);
};
x();

Question 2

var x = {
y: function() {
console.log(this);
}
}
x.y();

Question 3

var x = function() {
console.log(this);
}
var y = {
x: x
}
y.x();

Question 4

var x = {
y: function() {
console.log(this);
}
};
var a = {
b:x.y
};
a.b();

Question 5

(function() {
var x = function() {
console.log(this);
}
x();
})();

Question 6

(function() {
var x = function() {
console.log(this);
}
y = {};
y.x = x;
y.x();
})();

Question 7

(function() {
var x = function() {
console.log(this);
};
x.apply({a:'b'});
})();

Question 8

(function() {
var Cat = function() {
console.log(this);
};
var x = new Cat();
})();

Exercise - Fix the broken code

The following code is broken. There are a few ways it could be fixed, we are going to fix it by storing the value of "this" in a variable which we will call "that".

var noise = "woof";
var cats = {
noise: 'Meow',
sayMiow: function() {
setInterval(
function() {
console.log(this.noise);
},
1000
);
}
};
cats.sayMiow();

All about ES6 fat arrows (=>)

ES6 contains many nice little features. Fat arrows are perhaps one of the most surprising, and once you understand them, one of the most enjoyable.

Fat arrows give us a new, shorthand syntax for defining functions, and also a new way to initialise the this variable that comes into existence whenever a function is called.

Definition

We define a JavaScript function like this:

function() {}

An equivalent fat arrow function looks like this:

() => {}

A real example might look like this:

function (a,b) {
console.log(a+b);
};

which would become

(a,b) => {console.log(a+b)};

Exercise - Fat Fingers

Have a play with the above example at the babel repl here.

Convert the following into fat arrow format:

var sayHello = function() {
console.log('hello')
}
sayHello();

Do the same with this self executing function:

(function() {
var greeting = 'hello';
console.log(greeting);
})();

Exactly one parameter

If our function has exactly one parameter, we can skip the braces on the left hand side. Our function then looks like this:

a => {return a + 1}

This is equivalent to:

function(a) {
return a + 1;
}

Exercise - One Parameter

Have a play with the above example at the babel repl here.

Convert the following into fat arrow format:

var sayHello = function(name) {
return "Hello " + name;
}

Concise syntax

As if this wasn't short enough, We have a concise syntax. If our fuction contains exactly one line, we can also omit the curly braces and return statement, like so:

a => a + 1

which again is equivalent to:

function(a) {
return a + 1;
}

The evaluated value of the function expression is returned automatically. we don't need the return statement.

These sorts of functions are perfect for passing around, and this makes functional programming incredibly easy.

var i = 0;
setInterval( () => {i++}, 500 )

:markdown

Further Reading

MDN - Arrow functions

An Introduction to TypeScript

TypeScript is was first released by MicroSoft in 2012, so it's relatively new. It is a superset of JavaScript. All legal JavaScript is also legal TypeScript, so knowing TypeScript doesn't excuse you from knowing JavaScript.

however, if you are used to a classical, class based environment with type safety and decorators, TypeScript might be the tool for you.

Features

On top of JavaScript it also gives us features like:

  • Optional strong typing, with compile time errors if we mess up.
  • Classes (which are sugar for a special case of Prototypical inheritance).
  • Decorators - which are fabulously powerful.

We can have start to have a play with TypeScript in Microsoft's TypeScript playground. It gives us real time compilation, so we can see how to make things.

Classes

Let's start with a class. Here's a Toaster:

class Toaster {}

This compiles to a little JavaScript IIFE which generates the newable function and returns it:

var Toaster = (function () {
function Toaster() {
}
return Toaster;
}());

We use the new keyword to make a new object using the Toaster constructor, like so:

var myToaster = new Toaster();

Exercise - Create a class

Visit the TypeScript playground here:

http://www.typescriptlang.org/Playground

Now create a your own simple class. Take a look at the code that is generated. Use new to make a new object.

Attributes

JAvaScript objects are just Hashmaps, and so we can set any attributes we like on them. TypeScript gives us compile time safety, we can choose what attributes we want people to write to.

Say we do this:

class Toaster {}
var myToaster = new Toaster();
myToaster.bread = "Artisan rye splashed with swan's breath"

We get an error. Property bread does not exist on type Toaster.

This is a compile time error. The generated JavaScript will of course run just fine.

We tell the compiler to expect a bread attribute like so:

class Toaster {
bread
}

The error is now gone.

Exercise - Attributes

Give your new object an attribute. Create an instance using the new keyword. Check out the intellisense.

Constructors

A constructor function allows us to configure the new object. Here's our toaster with a constructor function. This constructor gets bread, saves it in an attribute, and console.logs it.

Note that we need to declare the bread attribute:

class Toaster {
bread
constructor(bread) {
this.bread = bread
console.log('Toast was made with bread', bread)
}
}

This compiles to the following. The constructor becomes the newable function returned from the IIFE:

var Toaster = (function () {
function Toaster(bread) {
this.bread = bread;
console.log('Toast was made with bread', bread);
}
return Toaster;
}());

We also have a shorthand for creating public parameters. We can set the paramter to public, and it will create the attribute for us.

class Toaster { constructor(public bread) {} }

Exercise - Constructors

Visit the TypeScript playground here:

http://www.typescriptlang.org/Playground

Add a constructor function to your class. See how it changes your JavaScript. Have your constructor receive a value and save it in an attribute.

Check out the public setting for the parameter.

Use new to create an object. Notice the intellisense as you interact with your new object.

Strong Typing

TypeScript gives us optional Strong Typing, like so:

class Toaster {
constructor(public bread:Bread) {}
}

Our Toaster will now only receive bread. If we try to pass it something else, the compiler will complain (though of course the generated code will still run.)

To make this work, we now need to declare Bread:

class Bread {}

Exercise - Strong Typing

Apply some strong typing to a constructor argument. Add in classes to make the compiler happy. Check out the code you get.

Building TypeScript

TypeScript gives us many significant advantages when working in larger teams. We get better integration with an IDE. We get compile time errors. We get strong typing.

In order to build TypeScript we will need quite a few tools. In this section we will use the tsc compiler to build our TypeScript.

NodeJS

First up, TypeScript is built using NodeJS. If you don't have it, you'll need to install it.

Go here and download the correct flavour for your system: https://nodejs.org

NodeJS is a JavaScript engine that runs in your console. Open a console and type node. You should drop into command line JavaScript.

NPM

Node has a package manager called NPM. We can use this to download our develoment dependencies.

Create a file called package.json, and pop the following into it. It's a list of dependencies that will let us run compile TypeScript into Angular modules:

{
"scripts": {
"tsc": "tsc -w",
"typings": "typings"
},
"dependencies": {
"angular2": "2.0.0-beta.6",
"systemjs": "0.19.20",
"es6-promise": "^3.0.2",
"es6-shim": "^0.33.3",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.0",
"zone.js": "0.5.14"
},
"devDependencies": {
"gulp-typescript": "^2.12.0",
"typescript": "^1.7.5",
"typings": "^0.6.8"
}
}

Run npm install to grab all the dependencies.

Typings

You may have noticed the typings dependency. This is a package manager for typescript definitions files.

We create a typings.json file like this:

{
"ambientDependencies": {
"es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#6697d6f7dadbf5773cb40ecda35a76027e0783b2"
}
}

Now we use typings to get those dependencies. Run:

npm run typings install

to install the es6 typings.

TS Config

Finally we need a tsconfig file. This tells TypeScript how to build.

Of particular importance are the decorators flags. These allow Angular to compile decorators, and to use them for automatic DI.

Create a tsconfig.json file now:

{
"compilerOptions": {
"target": "es5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
},
"exclude": [
"node_modules",
"typings/main",
"typings/main.d.ts"
]
}

Building TypeScript

This is sufficient to allow us to start building TypeScript.

Create an app folder and place a file inside it called app.ts. Now write any TypeScript inside.

To start compiling, run:

npm run tsc

Each time you save app.ts, app.js will be updated.

Exercise - Build TypeScript

Set up the above for automatic TypeScript compilation. Once you've done it one time, it will become easy. You can now use any editor to write your code.

Have a play with some TypeScript, see if it does what you expect.

Exercise - Visual Studio

Alternatively, if you don't want the hassle of working with NPM and the command line, set up visual studio to compile your TypeScript.

You will still need the tssonfig.js from above. Instructions can be found here:

Or For Visual Studio Code

Exercise - Atom

Atom is an editor that has been getting some traction lately. It has an excellent typescript plugin with intellisense, and syntax highighting, and has been described as "faster than visual studio".

If you'd like to have a try, you can get the typescript package here:

Building Angular Components with TypeScript

Now that we can compile TypeScript, it's time to build an app.

First up we'll need some HTML. There's quite a lot of required JavaScript. I'm assuming here that you have used NPM to install these JavaScript dependencies in the last chapter. If not, you'll need to download these manually, or link to the CDN.

<html>
<head>
<title>Hello World</title>
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/app')
.then(null, console.error.bind(console));
</script>
</head>
<body>
<app></app>
</body>
</html>

SystemJS

We will use SystemJS as a module loader here. This will allow us to put our modules in separate files, and require them via AJAX.

app.ts

Next we need our bootstrapper object that will start the whole thing. This will look something like this:

import {bootstrap} from 'angular2/platform/browser'
import {AppComponent} from './app.component'
bootstrap(AppComponent);

If you have your compiler running you will notice that it fails at this point because it can't find app.component. We'll make this now.

app.component.ts

App.component looks like this. Notice how, instead of ng.core.component, we use the @component decorator. Apart from this, all else is the same. We can still do inputs and events. We still have directives.

import {Component} from 'angular2/core';
@Component({
selector: 'app',
template: `
<h1>Hello World</h1>
`,
directives: []
})
export class AppComponent { }

Exercise - Implement the hello world above

Try to get the simple hello world running in a browser.

Harder Exercise - Tab Pane

Have a go at implementing your tab pane using TypeScript. Use the Hello World as a jumping off point, then rewrite the Tab Pane js files into TypeScript.

Even Harder Exercise - Flickr

Re-implement the Flikr search app using TypeScript.

Be sure to use separate files for each component. Break your app down as far as is sensible.

Building Services with TypeScript

In a TypeScript Angular app, a service is just a class. Generally the class will expose some data and some methods.

Creating a service

Here is a cat service. It lives in its own file called cat.service.ts

export class CatsService {
cats
constructor() {
this.cats = [
{ name: "Danger Moog" },
{ name: "Pippa T. Floof" },
{ name: "Sniff Weasel" }
]
}
get() {
return this.cats;
}
}

We run TSC to compile it into a JavaScript file.

Injecting this service

Now for the DI.

We could add this service to our global injector in our app.ts file like this:

import {bootstrap} from 'angular2/platform/browser'
import {AppComponent} from './app.component'
import {CatsService} from './cats.service';
bootstrap(AppComponent, [CatsService]);

(notice the CatsService between the braces, configuring the global injector.)

Hierarchal Injectors

This seems like overkill. Our CatsService doesn't need to be global to our app. It would be sufficient for it to be local to just the component that needs to access it.

We can create a child injector by adding a providers attribute of our component. providers: [CatsService]. Here's that in context in the catlist.component.ts file:

import {Component} from 'angular2/core';
import {CatsService} from './cats.service';
@Component({
selector: 'cat-list',
template: `
<ul>
<li *ngFor="#cat of cats">
{{cat.name}}
</li>
</ul>
`,
directives: [],
providers: [CatsService]
})
export class CatListComponent {
constructor(cats: CatsService) {
this.cats = cats.get();
}
}

Exercise - Create a service

Use the code above as a foundation. Refactor the Mario service, and port this to TypeScript. Be sure to use sensible components.

AJAX and TypeScript

In this section, we will use TypeScript to compose an AJAX service.

Creating the connection service

This service wishes to connect to the internet, and so needs to be able to talk to http. In order to do this we need to inject http into it.

As before, we're going to use JSONP, because we want to talk to a service that doesn't support CORS.

First we add the JSON providers to our global injector, like so (app.ts):

Making HTTP available

HTTP lives in a separate JavaScript file which you will need to include.

You will need to import node_modules/angular2/bundles/http.dev.js

Your HTML will look like this:

<html>
<head>
<title>AJAX</title>
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/http.dev.js"></script>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/app')
.then(null, console.error.bind(console));
</script>
</head>
<body>
<app></app>
</body>
</html>

Configuring the injector

I would like to make JSONP available globally, so I configure the injector in my app.ts file, like so:

import {bootstrap} from 'angular2/platform/browser'
import {AppComponent} from './app.component'
import {JSONP_PROVIDERS} from 'angular2/http'
bootstrap(AppComponent, [JSONP_PROVIDERS]);

Creating the Service and Making it Injectable

Now we can create our service. Here is a weather.service.ts

import {Injectable} from 'angular2/core';
import {Jsonp} from 'angular2/http';
@Injectable()
export class WeatherService {
cats
constructor(jsonp: Jsonp) {
this.url = "http://www.myweather.com/getweather" // Note this is not a real weather URL!
this.jsonp = jsonp;
}
get() {
return jsonp.get(this.url)
}
}

Notice the @Injectable decorator. This is essentially an empty decorator.

TypeScript will only emit typing metadata if there is a decorator that might depend on it. Using this decorator forces TypeScript to emit the metadata, which allows Angular to determine the types for injection.

Injecting the Service

Now we have our service, we can inject it into a weather component (weather.component.ts).

import {Component} from 'angular2/core';
import {WeatherService} from './weather.service';
@Component({
selector: 'weather',
template: `
<pre>{{weather | json}}</pre>
`,
directives: [],
providers: [WeatherService]
})
export class WeatherComponent {
constructor(weather: WeatherService) {
var vm = this;
weather.get()
.subscribe(function(response) {
vm.weather = response.json()
})
}
}

Exercise - Flickr

We are going to extend the Flickr code from earlier. Using the above as a base. grab a feed from Flickr, and us an *ngFor to render it on the page.

The flickr JSONP endpoint looks like this:

http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=JSONP_CALLBACK

Microsoft WebAPI

When implementing an API, we have 3 choices.

  1. Serve our templates from the same domain as the API to get around the same domain policy.
  2. Use CORS in IE10+. Optionally implement a polyfill for IE9.
  3. Use JSONP.

In this section we will implement a solution where the templates are served from the same domain.

Create a new asp.net web application. Then choose WebAPI as the project type.

Building a simple hardcoded API

We are now going to create a simple hardcoded web API.

  1. Go here and complete tasks 1 and 2. http://www.asp.net/web-api/overview/older-versions/build-restful-apis-with-aspnet-web-api#Exercise1
  2. Now we can visit a URL of this form and see a JSON feed: /api/contact.

Build a service to consume the API

We use an Angular 2 service to access our web API. Something like this:

import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
@Injectable()
export class UserService {
url
http
constructor(public http: Http) {
this.url = "http:localhost:1234/api/contacts"
this.http = http;
}
index() {
return this.http.get(this.url)
}
}

Build a RESTful API

We're now going to take this further and build a Restful API.

Go here and follow this. We don't need to deploy to Azure (doing so is optional). We just need to build the API.

https://azure.microsoft.com/en-us/documentation/articles/web-sites-dotnet-rest-service-aspnet-api-sql-database/

At the end we should be able to hit /api/contacts to get a list of contacts. We should also be able to post to the same URL to create a new contact.

Build a service to talk to the API

First up, build a service which can talk to the API. It will look something like the following:

import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
@Injectable()
export class UserService {
url
http
constructor(public http: Http) {
this.url = "http://www.mywebservice.com"
this.jsonp = jsonp;
}
get(id) {
return http.get([this.url,id].join('/'))
}
index() {
return http.get(this.url)
}
create(data) {
return http.post(this.url, data)
}
update(id, data) {
return http.patch([this.url,id].join('/'), data)
}
}

(You will need to update the URLs here.) Test this out in the console.

Build a Component

Next build a component which can talk to the API and pull a list of the contacts. Save the contacts on the compoennt. Write a view which can render them.

Create a form

Make a form which can create a new contact. Create a createContactComponent to manage the form. When the form is submitted, send the new contact.

Optional Extra - Extract the form into a template

If you extract the form into a template, you can reuse the same form to edit contacts.

Optionally create an EditContactFormComponent which will let you edit a contact in scope.

Optional Extra - A Spinner

For bonus points have the component display a spinner until the contact is ready to display.

Harder Exercise - CORS

CORS will allow us to serve templates from any URL. We can keep our front end and back end on different servers. We can use multiple APIs. we call this a component based architecture. It's a very nice way to do things.

You can find instructions to enable CORS here:

http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api#enable-cors

Try to enable CORS in your server.

Gulp

Gulp is a development automation tool. It's the successor to Grunt.

We configure gulp using a gulpfile. Our gulpfile contains a set of tasks that we would like to automate. Each task is a stream of activities, for example a js task might:

  1. Validate your code using JSHint.
  2. Concatenate your javascript.
  3. Save it as app.js.
  4. Reload your web browser so you can see the changes.
  5. Minify.
  6. Save the minified code as app.min.js.

Gulp streams

Gulp has a concept of streams. A stream is a set of files that can be transformed.

We create a stream using gulp.src, then pipe it through different functions which can transform the stream in a variety of ways. We can optionally pipe our stream back out onto the filesystem at any point using gulp.dest.

We use gulp for:

  • Validation
  • Compilation
  • Concatenation
  • Reformatting
  • Renaming
  • Wrapping content

Gulp modules

We extend the capabilities of Gulp using modules, which are installed using npm. Here are some useful ones:

  • jshint - JavaScript validation
  • tshint - TypeScript Static Code Analysis
  • sass - CSS precompilation
  • uglify - JavaScript minification
  • concat - Script concatenation
  • autoprefixer - Automatically add vendor prefixes to CSS
  • header - Adds a header to the file
  • size - Outputs the size of a minified file

Validating code

A common requirement is to validate our JavaScript. We can do this with a simple Gulp task:

var gulp = require('gulp'),
jshint = require('gulp-jshint');
gulp.task('assets:js', function () {
return gulp.src(components.js)
.pipe(jshint())
.pipe(jshint.reporter('default'))
}

We execute this task with:

gulp assets:js

Exercise - TSLint

We are going to install TSLint for static code analysis of our typescript files.

First Install Gulp and the gulp-cli using npm. Note that currently gulp has to be installed locally in your project, so we omit the -g flag.

npm install gulp --save-dev
npm install gulp-cli -g

Test your installation:

gulp --version

Now we're going to set up gulp to validate our TypeScript. In our project root, install the dependencies using npm:

npm install --save-dev gulp-tslint
npm install --save-dev tslint
npm install --save-dev typescript

Now create a gulpfile containing something like the following:

var gulp = require('gulp'),
tslint = require('gulp-tslint');
gulp.task('tslint', function () {
return gulp.src('app/**/*.ts')
.pipe(tslint())
.pipe(tslint.report("verbose"))
});

tslint.json

You will also need a tslint.json file to tell the linter how to behave, something like the following:

{
"rules": {
"class-name": true,
"curly": true,
"eofline": false,
"forin": true,
"indent": [true, 4],
"label-position": true,
"label-undefined": true,
"max-line-length": [true, 140],
"no-arg": true,
"no-bitwise": true,
"no-console": [true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-key": true,
"no-duplicate-variable": true,
"no-empty": true,
"no-eval": true,
"no-string-literal": false,
"no-trailing-whitespace": true,
"no-unused-variable": false,
"no-unreachable": true,
"no-use-before-declare": true,
"one-line": [true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"quotemark": [true, "single"],
"radix": true,
"semicolon": true,
"triple-equals": [true, "allow-null-check"],
"variable-name": false,
"whitespace": [true,
"check-branch",
"check-decl",
"check-operator",
"check-separator"
]
}
}

Now run gulp tslint to execute the task:

gulp tslint

Any errors? You may need to introduce an error to see the effect.

Automatic execution

We can tell gulp to watch our filesystem for changes, and execute a task whenever a file is modified.

gulp.task('watch', function() {
gulp.watch('app/**/*.ts', ['lint']);

Now we might create a default gulp task:

gulp.task('default', [
'watch'
]);

We can now set our gulp task running simply by typing gulp at a command line.

gulp

Exercise - Automatic Watching

Set up Gulp to automatically watch all your TypeScript files and execute the linter

SASS

SASS is Syntactically Awesome StyleSheets. It's similar to LESS, and most of what we learn here will also apply to LESS.

Refer to the documentation here: http://sass-lang.com/documentation/file.SASS_REFERENCE.html

CSS is valid SASS

One of the great things about SASS is that any CSS file is also valid SASS, so you can start using it right away.

a {
color:#f00;
}

Variables

We can define variables in sass, like so:

$red: #f00;
a {
color:$red;
}

Functions

SASS comes with a set of built in functions that we can use to transform variables, like so:

$red: #f00;
a {
color:darken($red, 30%);
}
a:hover {
color:lighten($red, 30%);
}

Maths

We can perform maths functions in our SASS to generate sizes. Here is a simple 2 column layout with a gutter.

$desktop: 1000px;
$gutter: 20px;
body {
width: $desktop;
}
article {
width: $desktop / 3 * 2 - $gutter;
float:left;
}
aside {
width: $desktop / 3 - $gutter;
float:right;
}

Nesting

We can nest our sass like so:

article {
a {
color: red;
}
strong {
font-weight: normal;
}
}

This will generate something like the following:

article a {
color: red;
}
article strong {
font-weight: normal;
}

Exercise - Gulp-sass

Use gulp-sass to compile sass. Have a look at the gulp-sass project and try to create a sass task to compile your sass. You may wish to concat it first.

Exercise - Nested Sass

Write nested queries to style the header with a horizontal nav bar, nicely positioned h1, and pretty background colour.

Exercise - Variables

  1. Create a $header_colour variable. Use it to set the background-color of your header.
  2. Create a $font_size variable (12px) and a $font_scale variable (1.5).
  3. Set the font-size to be the $font_size variable.
  4. Set the h2 font-size to be $font_size * $font_scale
  5. Set the h1 font-size to be $font_size * $font_scale ** 2
  6. Adjust the $font_size. See how everything updates. Woot!

Further Exercise - Mixins

Create a button mixin which sets a width, a height, a background-color, a padding and display:inline-block. Apply it to input type="submit", button, and a class="button"

Downloads

Example - Dropbox

Jade

Jade is an HTML templating language that's extremely popular in Node circles. You make fewer keystrokes to get the same result, which makes you a more productive coder. This is very Node.

First Install with NPM

To use Jade you must install the package. Do this with NPM.

npm install jade

Here's a gulpfile:

var gulp = require('gulp');
var jade = require('gulp-jade');
var gutil = require('gulp-util');
var components = {
jade: './jade/**/*.jade',
out: './build'
}
gulp.task('jade', function() {
return gulp.src(components.jade)
.pipe(jade({pretty:true}))
.on('error', gutil.log)
.pipe(gulp.dest(components.out));
})
gulp.task('watch', function() {
gulp.watch(components.jade, ['jade']);
});

Elements

The first word on any line is the html element:

h1 Hello there

Compiles to:

<h1>Hello there</h1>

Nesting elements

We can nest elements inside each other using indentation:

article
h1 Hello
p Hey!

Compiles to:

<article>
<h1>Hello</h1>
<p>Hey!</h1>
</article>

Attributes

We can create attributes:

a(href='http://www.lolcats.com/') LOL cats

Compiles to:

<a href="http://www.lolcats.com/">Lol cats</a>

Variables

We can include variables in our template using an equals sign like this if we just have one value:

article
h1= titleContent

or using curly brace interpolation like this if we want to embed content into text:

html
head
title myWebsite.com - #{titleContent}

Compiling Jade

We con compile Jade manually using the jade package. First we install it with npm:

npm install jade

Now we can compile our template. This gives us a template function:

var jade = require('jade');
var template = jade.compile('h1 hello #{name}');

Or from a file:

var template = jade.compileFile('./path/to/template.jade');

Finally we can compile our template into HTML, passing it an object full of include variables, like so:

template({name: "Davie Skitch Mulldoon"});

Jade with Gulp

We can use gulp to automatically compile jade into static html templates.

We use the gulp-jade package

gulp.task('assets:templateCache', function () {
return gulp.src('../jade/**/*.jade')
.pipe(jade({basedir: __dirname}))
.on('error', gutil.log)
.pipe(gulp.dest('../build/templates'));
});

Angular Exercise

Create a gulp task to automatically compile a folder full of jade into a folder full of html.

Extension

Try to integrate gulp-template-cache to generate an angular template module from a folder full of jade.

Watch & Reload

We can use gulp to watch a set of files and automatically run a task when any of them change.

For example we might watch our js directory and run the js task.

gulp.task('watch', function() {
gulp.watch('./js/**/*.js', ['js']);
});

We can then start our watchers using gulp watch.

Combination tasks

We can make a task depend on other tasks. This can help us split our tasks up into smaller pieces, like so:

gulp.task('build', [
'js',
'sass',
'lib',
]);

Now we run:

gulp build

and this will run all the tasks.

Default task

We can set a default task that will be run when we call gulp:

gulp.task('default', [
'build',
'watch'
]);

Now we only need to run:

gulp

to build and turn on the watchers.

Livereload

Live reload will automatically reload your web page when a task completes. It comes in two parts:

  • The gulp-livereload plugin that manages the livereload server
  • A Chrome plugin that listens for a reload

If you install both of these you can chain live-reload into your tasks and have gulp reload your browser automatically on file change.

This works extremely well when multi-screening.

gulp.task('assets:js', function () {
return gulp.src('./js/**/*.js')
.pipe(concat('app.js'))
.pipe(gulp.dest('./build'))
.pipe(livereload());
});

Exercise - Livereload

  • Create a gulp watch task which watches your js.
  • Create a default task so you can just call gulp to start watching.
  • integrate live-reload