diff --git a/public/docs/ts/latest/cookbook/_data.json b/public/docs/ts/latest/cookbook/_data.json index e4363318ef..f9f973b5bd 100644 --- a/public/docs/ts/latest/cookbook/_data.json +++ b/public/docs/ts/latest/cookbook/_data.json @@ -4,13 +4,13 @@ "navTitle": "Overview", "description": "A collection of recipes for common Angular application scenarios" }, - + "a1-a2-quick-reference": { "title": "Angular 1 to 2 Quick Reference", "navTitle": "Angular 1 to 2 Quick Ref", "intro": "Learn how Angular 1 concepts and techniques map to Angular 2" }, - + "component-communication": { "title": "Component Interaction", "intro": "Share information between different directives and components" @@ -29,5 +29,10 @@ "ts-to-js": { "title": "TypeScript to JavaScript", "intro": "Convert Angular 2 TypeScript examples into ES5 JavaScript" + }, + + "esnext": { + "title": "ESNext Primer", + "intro": "Learn about ES6+ features and how they are used with Angular" } } diff --git a/public/docs/ts/latest/cookbook/esnext.jade b/public/docs/ts/latest/cookbook/esnext.jade new file mode 100644 index 0000000000..e3ba1469a2 --- /dev/null +++ b/public/docs/ts/latest/cookbook/esnext.jade @@ -0,0 +1,492 @@ +include ../_util-fns + +- var top="vertical-align:top" + +:marked + # Who is this guide for? + + This guide is intended to provide developers who are unfamiliar with ES6 and want to learn the + primary features used in Angular. This is not a complete guide to all of the new features + available to you, but a tour of the most commonly used with Angular. + + It is possible to write Angular using ES5 syntax (the common version of JavaScript since 2009), + so why should you bother to learn ES6 anyways? + + * Angular 2 was designed with using ES6 syntax in mind. + * It provides improved clarity and consistency in coding. + * Many examples and tutorials are written in ES6. + * You can choose to use as much or as little as you wish. + + Many developers find using ES6 increases code quality, makes it easier to read code and understand + intention, and can help enforce some best practices. Let's take a look at the primary features, + and see how they are used with Angular. + + ## Classes + + Classes provide a new syntax for declaring objects. They are primarily used when creating Angular + entities such as components and services. Classes are purely a new syntax, they do not introduce + new features. Let's see a class and compare it to a similar approach using a constructor function + like you would write with ES5. + +table(width="100%") + col(width="50%") + col(width="50%") + tr + th Class + th ES5 Constructor Function + tr(style=top) + td + :marked + ``` + class Person { + constructor(name) { + this.name = name; + } + + getName() { + return this.name; + } + } + var user = new Person('Jeremy'); + user.getName(); // Returns 'Jeremy' + ``` + td + :marked + ``` + function Person (name) { + this.name = name; + } + + Person.prototype.getName = function() { + return this.name; + }; + + var user = new Person('Jeremy'); + user.getName(); // Returns 'Jeremy' + ``` + +:marked + In this example, we define a new class called `Person`. The `constructor()` method executes as soon + as the class is instanciated using the `new` keyword, and can accept arguments. Methods are defined + like a function, but without using the keyword `function`. + + The biggest advantages of a class are to provide a consistency in the syntax for declaring objects, + and can help enforce some best practices like strict mode. + + ## Modules + + Modules enable code encapsulation and the ability to import objects from other files. Most other + languages have had the concept of modules (perhaps under another name), such as Ruby's `require` or + Java's `import`. However, JavaScript has lacked a native module system. + + There are some module systems for JavaScript, primarily CommonJS and AMD. Another option is use + of a global variable that acts as a namespace for various shared objects. With native JavaScript + modules, we now have a clean syntax for importing modules that looks like this. + +table(width="100%") + col(width="50%") + col(width="50%") + tr + th Export (person.js) + th Import (user.js) + tr(style=top) + td + :marked + ``` + export class Person { + constructor(name) { + this.name = name; + } + + getName() { + return this.name; + } + } + ``` + td + :marked + ``` + import {Person} from './user'; + + var user = new Person('Jeremy'); + user.getName(); // Returns 'Jeremy' + ``` + +:marked + First, it is important to note that any file that has the `import` or `export` keywords are + automatically considered modules. The code inside of those files will not be available in the + global scope. + + On the left side, we have a file that contains our `Person` class. We add the `export` keyword + to declare that this object is to be made available. Since the `export` keyword was used, this file + is considered a module. + + The `import` keyword allows us to bring in a value from another location, and on the right side + you can see the syntax to load in the `Person` class. Again, the use of `import` makes this file + a module as well, so the variable `user` is not assigned to the global scope. + + Modules are a very important feature that helps us write better code that is encapsulated and isolated. + By avoiding code in the global scope, we can protect the privacy of code while still being able to share. + + ## Decorators + + Decorators are a way to declaratively add metadata to an object. Decorators are essentially functions + that receive a copy of the object and modify it. The concept is found in some other + languages, such as Python decorators or Java annotations. Decorators always start with the `@` symbol, + so they should be easy to spot, and they can be defined for a class or property. + + Let's make an example that decorates the `Person` class that assigns a property of `isHero` to indicate + if they are a hero. Let's imagine there is a decorator called `@Hero()` and use it in our example. + +table(width="100%") + col(width="50%") + col(width="50%") + tr + th Using a Decorator (person.js) + th Import (user.js) + tr(style=top) + td + :marked + ``` + @Hero() // Decorator used on Person + export class Person { + constructor(name) { + this.name = name; + } + + getName() { + return this.name; + } + } + ``` + td + :marked + ``` + import {Person} from './user'; + + var user = new Person('Jeremy'); + user.getName(); // Returns 'Jeremy' + user.isHero; // Returns true thanks to decorator + ``` + +:marked + This example allows us to see the power of decorators. Angular already defines several of these + to use when creating objects, such as `@Component()`, `@Directive()`, and `@Pipe()`. + + Technically, decorators are not officially part of the ECMAScript standard yet, but they are likely + to be implemented in an upcoming version of JavaScript. However, most compilers support it anyways. + + ## `let` and `const` Keywords + + There are two new ways to declare variables by using the `let` and `const` keywords. Until ES6, + a defined variable would be confined to the function it was defined within, or the global scope if + defined outside of a function. So when you use `var` the variable is available inside of the function. + + On the other hand, with ES6 variables defined using `let` and `const` are both block scoped. The concept + of block statement scope is new in ES6, and allows us to limit the scope for better encapsulation. Block + scope is essentially any place there are brackets `{}` enclosing some code, for example an `if`/`else` + block or `for` loop block. + + The `let` keyword will define a variable in the current block scope, and can easily replace the use of + `var` in most contexts. + + The `const` keyword also defines a variable in the current block scope, but is special in that you cannot + reassign a new value to the variable after it has been defined. You can modify the existing value, but you + are not allowed to Let's see how it is used in an example. + + ``` + import {Person} from './user'; + const powers = ['flight', 'x-ray vision', 'invisibility']; // Has scope of entire module, cannot be reassigned + + function pickRandomPower() { + let index = Math.floor(Math.random() * (powers.length + 1)); // Has scope of only function + let power = powers[index]; // Can access the variable from a higher scope + powers.splice(index, 1); // Can mutate the value of the const powers + return power; + } + + let user = new Person('Jeremy'); // Has scope of entire module, can be reassigned + if (user.isHero) { + let power = pickRandomPower(); // Only available inside of if statement, doesn't conflict with above + user.power = power; + } + + // Things you can't do + if (power === 'flight') { Cannot access `power` because it is limited to if blocks above + ... + } + powers = ['faster than bullet', 'premonitions', ''] // Cannot reassign the value of a const + ``` + + These two new ways to define variables are recommended since they enforce a more restrictive scope + onto the variables and help prevent variables from bleeding out of the scope they were defined. + + ## Arrow Functions + + One of the more difficult aspects of JavaScript is knowing what the keyword `this` currently refers to, + since it depends on the current context. With classes, it becomes easier to reason about how `this` works. + You may recall in the class example that `this.name` was used inside of the constructor and method, meaning + we can be sure they shared the same scope context. + + However, there are still situations where we'll need to write functions inside of our class methods. Often + we will want to have this inner function to retain the same context so we can assign values to the same + `this`. + + Arrow functions simply take the place of the traditional `function` keyword, and do not create a new scope. + The syntax for an arrow function has several possible formats. + + ``` + items.forEach((item) => { return item++ }); // Typical format, `() => {}` + items.forEach(item => { return item++ }); // Can optionally drop parenthenses when defining parameters + items.forEach((item) => item++); // Can optionally drop curly braces if the result of the statement is returned + ``` + + There are a whole lot of conditions with arrow functions. + + * They are always anonymous. You cannot name an arrow function. + * They do not bind `this`, instead use the same context. + * There is no `arguments` object for an arrow function, it may be available from a parent function though. + * The body of the function can be concise (implied return) or a block body (explicit return). + + Let's add a method to our class that will be able to load a person's biography. This conceptually uses the + Angular [Http client](../guide/server-communication.html), which you can review if you aren't familiar with it. + On the left, you can see the arrow function syntax in the `getBio()` method, and on the right you see a + common workaround to keep track of the correct context. + +table(width="100%") + col(width="50%") + col(width="50%") + tr + th Arrow functions in ES6 + th Workaround for ES5 + tr(style=top) + td + :marked + ``` + import {Http} from 'angular2/http'; + + export class Person { + constructor(@Inject('Http') http) { + this.http = http; + this.skills = []; + } + + getSkills() { + // Here the then promise resolve function is an arrow function, in the shorted syntax possible + this.http.get('/api/bio') + .then(response => this.bio = response.json()); + } + } + ``` + td + :marked + ``` + import {Http} from 'angular2/http'; + + export class Person { + constructor(@Inject('Http') http) { + this.http = http; + this.skills = []; + } + + getSkills() { + // Bind `this` into the inner function to share same scope + this.http.get('/api/bio').then(function(response) { + // Inside of new scope, so have to use `that` to reference class + that.bio = response.json() + }.bind(this)); + } + } + ``` + +:marked + There are other ways to work around this limitation, such as assigning `this` to a variable and + using it to reference the parent context. However, arrow functions provide a new syntax + to declare functions that do not create their own scopes. + + Not all functions should be written as arrow functions. If there is no reason to use the same scope, + then a normal function is a better option. Keep in mind since arrow functions are anonymous, any stack + traces will show anonymous functions instead of function names. + + ## Template literals + + With Angular, often developers write their HTML partial templates and store that in an external HTML file. + ES6 provides support for multiline strings (called template literals) using the back-tick character, + so you could also store them inline with your JavaScript code should you desire. + + These multiline strings can contain HTML characters, and even some basic expression interpolation. Normally + you won't use this with Angular, but it is available. Here is a comparision of how the template literal syntax + works, most commonly to be used with defining a template for a component. + +table(width="100%") + col(width="50%") + col(width="50%") + tr + th ES6 Template literals + th ES5 Strings + tr(style=top) + td + :marked + ``` + // With ES6, the whole template exists between back-ticks + // and can even have simple interpolation + @Component( + selector: 'person', + template: `