chore(jade): upgrade to use marked filter

The `Transformers.markdown` (`:markdown`) filter is deprecated. This commit updates
the entire docs project to use the `jstransformer-marked` (`:marked') filter.
This commit is contained in:
Peter Bacon Darwin
2015-11-10 18:37:53 +00:00
parent 0f61ea288f
commit e86fde8dc9
31 changed files with 1866 additions and 1865 deletions
+3
View File
@@ -60,5 +60,8 @@
"q": "^1.4.1",
"typescript": "~1.5.3",
"yargs": "^3.23.0"
},
"dependencies": {
"jstransformer-marked": "^1.0.1"
}
}
@@ -11,7 +11,7 @@ include ../../../_includes/_util-fns
.showcase-content
.l-sub-section
:markdown
:marked
### Including a code example from the `_examples` folder
One of the design goals for this documention was that any code samples that appear within the documentation be 'testable'.
@@ -35,7 +35,7 @@ include ../../../_includes/_util-fns
code-example(language="js").
include ../../../../_includes/_util-fns
:markdown
:marked
The syntax for the `makeExample` mixin is:
#### +makeExample(filePath, region, title, stylePattern)
@@ -49,14 +49,14 @@ include ../../../_includes/_util-fns
code-example(format="linenums" language="js").
+makeExample('styleguide/js/index.html', null, 'index.html')
:markdown
:marked
This will read the *_examples/styleguide/js/index.html* file and include it
with the heading 'index.html'. Note that the file will be properly escaped and
color coded according to the extension on the file ( html in this case).
+makeExample('styleguide/js/index.html', null, 'index.html')
:markdown
:marked
The second parameter with a value of 'null' will be described later in this document.
There is a similar `makeTabs` mixin that provides the same service but for multiple examples
@@ -73,13 +73,13 @@ include ../../../_includes/_util-fns
code-example(format="linenums" language="js").
+makeTabs('styleguide/js/index.html, styleguide/js/spec.js', null, 'index.html,unit test')
:markdown
:marked
This will create two tabs, each with its own title and appropriately color coded.
+makeTabs('styleguide/js/index.html, styleguide/js/spec.js', null, 'index.html,unit test')
.l-sub-section
:markdown
:marked
### Marking up an example file for use by the `makeExample` and `makeTabs` mixins
At a minimum, marking up an example file simply consists of adding a single comment line to the top of the file
@@ -98,7 +98,7 @@ include ../../../_includes/_util-fns
});
});
:markdown
:marked
If a file only has a single `#docregion` then the entire file AFTER the `#docregion` comment is available for inclusion
via mixin. Portions of the file can be indicated by surrounding an area of the file with
`#docregion` and an `#enddocregion` tags. These regions, each with its own name, may be nested to any level and any regions that are not 'ended' explicitly
@@ -130,7 +130,7 @@ include ../../../_includes/_util-fns
// #enddocregion
// #enddocregion
:markdown
:marked
Multiple `#docregion` tags may be defined on a single line as shown below. In addition, anytime a file contains multiple
`#docregion` tags with the same name they will automatically be combined. Each of the individually tagged sections of the combined document
will be separated from one another by a comment consisting of '. . .'. This default separator, known
@@ -158,7 +158,7 @@ include ../../../_includes/_util-fns
doSomethingInteresting();
// #enddocregion
:markdown
:marked
HTML files can also contain #docregion comments:
code-example(format="linenums" language="html" escape="html").
@@ -168,7 +168,7 @@ include ../../../_includes/_util-fns
<script src="app.js"></script>
...
:markdown
:marked
as can CSS files:
code-example(format="linenums" language="css").
@@ -179,7 +179,7 @@ include ../../../_includes/_util-fns
}
.l-sub-section
:markdown
:marked
### Including a named #docregion via the makeExample or makeTabs mixins.
In order to include just a portion of an example file that has been marked up with a 'named' `#docregion`
@@ -189,14 +189,14 @@ include ../../../_includes/_util-fns
code-example(format="linenums" language="js").
+makeExample('styleguide/js/app.js', 'class-w-annotations', "Extracted region")
:markdown
:marked
is a request to include just the `class-w-annotations` region from the `app.js` file in the `_examples/styleguide`
folder and results in the following:
+makeExample('styleguide/js/app.js', 'class-w-annotations', "Extracted region")
.l-sub-section
:markdown
:marked
### Additional styling
In some cases you may want to add additional styling to an external file after it had been included in the documentation.
@@ -215,7 +215,7 @@ include ../../../_includes/_util-fns
code-example(format="linenums" language="js" escape="none").
+makeExample('styleguide/js/index.html', null, 'index.html', {pnk: /script (src=.*&ampquot;)/g})
:markdown
:marked
Which will mark all of the quoted contents of each `script` tag within the index.html file in pink.
.alert.is-important.
@@ -224,20 +224,20 @@ include ../../../_includes/_util-fns
+makeExample('styleguide/js/index.html', null, 'index.html', {pnk: /script (src=.*&quot;)/g})
:markdown
:marked
A more complicated example might be:
code-example(format="linenums" language="js").
- var stylePattern = { pnk: /script (src=.*&ampquot;)/g, otl: /(\S*my-app.*$)/m };
+makeExample('styleguide/js/index.html', null, 'index.html', stylePattern )
:markdown
:marked
Which applies multiple styles and uses an intermediate javascript object as opposed to a literal.
- var stylePattern = { pnk: /script (src=.*&quot;)/g, otl: /(\S*my-app.*$)/m };
+makeExample('styleguide/js/index.html', null, 'index.html', stylePattern )
:markdown
:marked
`makeTabs` support for `stylePatterns` is slightly different from the `makeExample` mixin in that you can also
pass in an array of stylePattern objects where each is paired with its corresponding 'tab'. If only a single stylePattern
object is passed in then it is assumed to apply to all of the tabs.
@@ -250,7 +250,7 @@ include ../../../_includes/_util-fns
+makeTabs('styleguide/js/index.html, styleguide/js/spec.js', null, 'index.html,unit test', stylePatterns)
.l-sub-section
:markdown
:marked
### Including a JSON file or just parts of one
To include an '.json' file from somewhere in the `doc\_examples` folder you can use the `makeJson` mixin. The `makeExample`
@@ -277,7 +277,7 @@ include ../../../_includes/_util-fns
+makeJson('styleguide/package.json', null, "Entire package.json file")
:markdown
:marked
A subset of the '.json' file can also be selected.
code-example(format="linenums" language="js").
@@ -285,7 +285,7 @@ include ../../../_includes/_util-fns
+makeJson('styleguide/package.json', { paths: 'version, scripts.tsc, scripts.start '}, "Selected parts of the package.json file" )
:markdown
:marked
Styling selected portions of the json is also supported.
code-example(format="linenums" language="js").
@@ -293,7 +293,7 @@ include ../../../_includes/_util-fns
+makeJson('styleguide/package.json', {paths: 'dependencies'}, "package.json dependencies", { pnk: [/(\S*traceur.*)/, /(\Sangular2.*)/, /(\Ssystem.*)/ ]})
:markdown
:marked
As well as styling across multiple lines.
code-example(format="linenums" language="js").
@@ -304,7 +304,7 @@ include ../../../_includes/_util-fns
+makeJson('styleguide/package.json', {paths: 'name, version, dependencies '}, "Foo", styles )
.l-sub-section
:markdown
:marked
### Inline code and code examples provided directly i.e. not from an example file.
The `makeExample` and `makeTabs` mixins are both both built on top of a custom jade 'style'; `code-example`.
@@ -391,7 +391,7 @@ include ../../../_includes/_util-fns
// TAB 2 CONTENT
.l-sub-section
:markdown
:marked
### Combining makeExample, makeTabs mixins with code-example style attributes
As mentioned above the `makeExample` and `makeTabs` mixins are built on top of the `code-example` style. By default
the mixins automatically determine a language based on the example file's extensions and always include line numbers.
@@ -403,12 +403,12 @@ include ../../../_includes/_util-fns
code-example().
+makeExample('styleguide/js/app.js', "class-w-annotations")(format="linenums:15")
:markdown
:marked
Starts the numbering of the example at line 15.
+makeExample('styleguide/js/app.js', "class-w-annotations")(format="linenums:15")
:markdown
:marked
Or to suppress line numbering completely you can use
code-example().
@@ -417,7 +417,7 @@ include ../../../_includes/_util-fns
+makeExample('styleguide/js/app.js', 'class-w-annotations')(format=".")
.l-sub-section
:markdown
:marked
### Code examples in angular/angular source code
References to embedded example code in the angular/angular source make use of the same mixins as defined above, but with a slightly different
@@ -431,7 +431,7 @@ include ../../../_includes/_util-fns
Example files referenced by inline tags are all assumed to be in the 'modules/angular2' folder in the angular/angular repo.
:markdown
:marked
#### @example inline tag parameters
- *filePath:* path to the example file under the '_examples' folder
- *region:* (optional or null) region from the example file to display
@@ -452,7 +452,7 @@ include ../../../_includes/_util-fns
* {@example core/directives/ng_if_spec.ts foo title='Foo' }
**/
:markdown
:marked
#### @exampleTabs inline tag parameters
- *filePaths:* a comma delimited string of filePaths to example files under the '_examples' folder
- *regions:* (optional or null) region from the example file to display
@@ -469,7 +469,7 @@ include ../../../_includes/_util-fns
**/
.l-sub-section
:markdown
:marked
### Cross references to Developer guide pages in angular/angular source comments.
The '{@linkDevGuide ... }' inline tag is intended to be used to create links from api documentation to dev guide
+8 -8
View File
@@ -1,11 +1,11 @@
include ../../../_includes/_util-fns
:markdown
:marked
Let's start from zero and build a super simple Angular 2 application in Dart.
.callout.is-helpful
header Don't want Dart?
:markdown
:marked
Although we're getting started in Dart, you can also write Angular 2 apps
in TypeScript and JavaScript.
Just select either of those languages from the combo-box in the banner.
@@ -27,7 +27,7 @@ p.
.l-main-section
h2#section-install-angular Set up a new app directory
:markdown
:marked
Create a new directory,
and put a file named `pubspec.yaml` in it.
@@ -73,7 +73,7 @@ p.
+makeExample('quickstart/dart/ex1/web/main.dart', null, 'web/main.dart')
:markdown
:marked
You've just defined an Angular 2 **component**,
one of the most important Angular 2 features.
Components are the primary way to create application views
@@ -121,7 +121,7 @@ p.
+makeExample('quickstart/dart/ex1/web/index.html', null, 'web/index.html')
:markdown
:marked
The `<my-app>` tag in the `<body>` is
the custom HTML element defined in the Dart file.
@@ -149,7 +149,7 @@ p.
Once the app is running,
you should see <b>My First Angular 2 App</b> in your browser window.
:markdown
:marked
If you don't see that, make sure you've entered all the code correctly
and run `pub get`.
@@ -158,14 +158,14 @@ p.
h2#section-angular-run-app Generate JavaScript
:markdown
:marked
Before you can deploy your app, you need to generate JavaScript files.
Pub build makes that easy.
To improve your app's performance, convert the
HTML file to directly include the generated JavaScript;
one way to do that is with dart_to_js_script_rewriter.
:markdown
:marked
Add the dart_to_js_script_rewriter package to your pubspec,
in both the `dependencies` and `transformers` sections.
+48 -48
View File
@@ -1,54 +1,54 @@
include ../../../_includes/_util-fns
:markdown
:marked
Let's start from zero and build a simple Angular 2 application in JavaScript.
.callout.is-helpful
header Don't want JavaScript?
:markdown
:marked
Although we're getting started in JavaScript, you can also write Angular 2 apps
in TypeScript and Dart by selecting either of those languages from the combo-box in the banner.
:markdown
:marked
We'll do it in six short steps
1. Create a project folder
1. Install essential libraries
1. Write the root component for our application in *app.js*
1. Bootstrap the app
1. Bootstrap the app
1. Create an *index.html*
1. Run it
.l-main-section
:markdown
:marked
## Create a project folder
**Create a new folder** to hold our application project, perhaps like this:
```
mkdir angular2-quickstart
cd angular2-quickstart
```
.l-main-section
:markdown
:marked
## Install essential libraries
We'll use the **npm package manager** to install packages for
the libraries and development tools we need:
the libraries and development tools we need:
>angular2 - the Angular 2 library.
>[live-server](https://www.npmjs.com/package/live-server "Live-server")
>[live-server](https://www.npmjs.com/package/live-server "Live-server")
a static file server that reloads the browser when files change.
We could reference the libraries on the web or download them to our project.
We could reference the libraries on the web or download them to our project.
That isn't a sustainable development process and package loading with npm is really
easy once we have it installed.
.alert.is-helpful
:markdown
.alert.is-helpful
:marked
Don't have npm? [Get it now](https://docs.npmjs.com/getting-started/installing-node "Installing Node.js and updating npm")
because we're going to use it now and repeatedly throughout this documentation.
:markdown
:marked
**Open** a terminal window and enter these commands:
```
npm init -y
@@ -61,24 +61,24 @@ include ../../../_includes/_util-fns
+makeJson('quickstart/js/package.json', { paths: 'name, version, dependencies, devDependencies'})
:markdown
:marked
There is also a `scripts` section. **Find and replace** it with the following:
+makeJson('quickstart/js/package.json', { paths: 'scripts'})
:markdown
:marked
We've just extended our project world with a script command that we'll be running very soon.
.l-main-section
:markdown
:marked
## Our first Angular component
Add a new file called *app.js* and paste the following lines:
+makeExample('quickstart/js/app.js', 'class-w-annotations')
:markdown
We're creating a visual component named **`AppComponent`** by chaining the
:marked
We're creating a visual component named **`AppComponent`** by chaining the
`Component` and `Class` methods that belong to the **global Angular namespace, `ng`**.
```
@@ -88,12 +88,12 @@ include ../../../_includes/_util-fns
```
The **`Component`** method takes a configuration object with two
properties. The `selector` property tells Angular that this is a component
controlling a host element named "my-app".
controlling a host element named "my-app".
Angular creates and displays an instance of our `AppComponent`
wherever it encounters a `my-app` element.
The `template` property defines the visual appearance of the component.
We're writing the HTML template inline in this example.
We're writing the HTML template inline in this example.
Later we'll move the HTML to a view template file and
assign the template's filename to the `templateUrl` property.
We'll prefer that practice for all but the most trivial templates.
@@ -101,24 +101,24 @@ include ../../../_includes/_util-fns
The **`Class`** method is where we implement the component itself,
giving it properties and methods that bind to the view and whatever
behavior is appropriate for this part of the UI.
This component class has the bare minimum implementation:
a *no-op* constructor function that does nothing because there is nothing to do.
We'll see more interesting component classes in future examples.
.l-main-section
:markdown
:marked
## Bootstrap the app
We need to do something to put our application in motion.
Add the following to the bottom of the `app.js` file:
+makeExample('quickstart/js/app.js', 'bootstrap')
:markdown
:marked
We'll wait for the browser to tell us that it has finished loading
all content and then we'll call the Angular `bootstrap` method.
The `bootstrap` method tells Angular to start the application with our
The `bootstrap` method tells Angular to start the application with our
`AppComponent` at the application root.
We'd be correct to guess that someday our application will
consist of more components arising in tree-like fashion from this root.
@@ -126,22 +126,22 @@ include ../../../_includes/_util-fns
### Wrapped in an IIFE
We don't want to pollute the global namespace.
We don't need an application namespace yet.
So we'll surround the code in a simple IIFE
("Immediately Invoked Function Execution")
So we'll surround the code in a simple IIFE
("Immediately Invoked Function Execution")
wrapper.
Here is the entire file:
+makeExample('quickstart/js/app.js', 'dsl')
.l-main-section
:markdown
:marked
## Create an *index.html*
**Add a new `index.html`** file to the project folder and enter the following HTML
+makeExample('quickstart/js/index.html', null, 'index.html')(format="")
.l-sub-section
:markdown
:marked
Our app loads two script files in the `<head>` element:
>***angular2.sfx.dev.js***, the Angular 2 development library that puts
@@ -149,15 +149,15 @@ include ../../../_includes/_util-fns
>***app.js***, the application JavaScript we just wrote.
In the `<body>`, there's an element called `<my-app>`.
In the `<body>`, there's an element called `<my-app>`.
This is the placeholder for the *root* of the
application. Angular displays our application here.
.l-main-section
:markdown
:marked
## Run it!
We need a file server to serve the static assets of our application
We need a file server to serve the static assets of our application
(*index.html* and *app.js*).
For this example we'll use the **live-server** that we installed with `npm`
@@ -170,27 +170,27 @@ include ../../../_includes/_util-fns
code npm start
.alert.is-helpful
:markdown
:marked
That's the `npm` command we added earlier to the `scripts` section of `package.json`
:markdown
:marked
**live-server** loads the browser for us and refreshes the page as we make
changes to the application.
In a few moments, a browser tab should open and display
figure.image-display
img(src='/resources/images/devguide/quickstart/my-first-app.png' alt="Output of quickstart app")
:markdown
:marked
### Make some changes
The `live-server` detects changes to our files and refreshes the browser page for us automatically.
Try changing the message to "My SECOND Angular 2 app".
Try changing the message to "My SECOND Angular 2 app".
The `live-server` sees that change and reloads the browser.
Keep `live-server` running in this terminal window and keep trying changes.
Keep `live-server` running in this terminal window and keep trying changes.
You can stop it anytime with `Ctrl-C`.
**Congratulations! We are in business** ... and ready to take
our app to the next level.
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,7 @@ include ../../../../_includes/_util-fns
<!-- http://plnkr.co/edit/x9JYbC -->
:markdown
:marked
## Displaying Component Properties
We typically display data in Angular by binding controls in an HTML template
@@ -17,7 +17,7 @@ figure.image-display
img(src="/resources/images/devguide/displaying-data/final.png" alt="Final UI")
.l-main-section
:markdown
:marked
## Showing component properties with interpolation
The easiest way to display a component property
is to bind the property name through interpolation.
@@ -32,7 +32,7 @@ figure.image-display
+makeExample('displaying-data/ts/src/app/app.1.ts')
:markdown
:marked
We added two properties to the formerly empty component: `title` and `myHero`.
Our revised template displays the two component properties using the double curly brace
@@ -40,24 +40,24 @@ figure.image-display
+makeExample('displaying-data/ts/src/app/app.1.ts', 'template')
.l-sub-section
:markdown
:marked
The template is a multi-line string within ECMAScript 2015 back-tics (\`).
The back-tick (\`) is not the same character as a single quote (').
It has many nice features. The feature we're exploiting is
the ability to compose the string over several lines which
makes for much more readable HTML.
:markdown
:marked
Angular automatically pulls the value of the `title` and `myHero` properties from the component and
inserts those values into the browser. Angular will update the display
when these properties change.
.l-sub-section
:markdown
:marked
More precisely, the re-display occurs after some kind of asynchronous event related to
the view such as a keystroke, a timer completion, or an asynch `XHR` response.
We don't have those in this sample.
But then the properties aren't changing on their own either. For the moment we must operate on faith.
:markdown
:marked
Notice that we haven't called **new** to create an instance of the `AppComponent` class.
Angular is creating an instance for us. How?
@@ -65,7 +65,7 @@ figure.image-display
Remember back in QuickStart that we added the `<my-app>` element to the body of our `index.html`
+makeExample('displaying-data/ts/src/index.html', 'my-app')
:markdown
:marked
When we bootstrap with the `AppComponent` class (see the bottom of `app.ts`), Angular looks for a `<my-app>`
in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it
inside the `<my-app>` tag.
@@ -90,40 +90,40 @@ figure.image-display
Some folks prefer to declare the properties and initialize them within a constructor like this:
+makeExample('displaying-data/ts/src/app/app-ctor.ts', 'app-ctor')
:markdown
:marked
That's fine too. The choice between them is a matter of taste and organization policy.
We'll adopt the more terse "variable assignment" style in this chapter simply because
there will be less code to read.
.l-main-section
:markdown
:marked
## Showing an array property with NgFor
We want to display a list of heroes. We begin by adding a mock heroes name array to the component,
just above `myHero` and redefine `myHero` to be the first name in the array.
+makeExample('displaying-data/ts/src/app/app.2.ts', 'mock-heroes')
:markdown
:marked
Now we use the Angular `NgFor` "repeater" Directive in the template to display
each item in the `heroes` list.
+makeExample('displaying-data/ts/src/app/app.2.ts', 'template')
:markdown
:marked
Our presentation is the familiar HTML unordered list with `<ul>` and `<li>` tags. Let's focus on the `<li>` tag.
+makeExample('displaying-data/ts/src/app/app.2.ts', 'li-repeater')
:markdown
:marked
We added a somewhat mysterious `*ng-for` to the `<li>` element.
That's the Angular "repeater" directive.
It's presence on the `<li>` tag marks that `<li>` element (and its children) as the "repeater template".
.alert.is-important
:markdown
:marked
Don't forget the leading asterisk (\*) in front of `*ng-for`. It is an essential part of the syntax.
Learn more about this and `NgFor` in the [Template Syntax](./template-syntax.html#ng-for) chapter.
:markdown
:marked
Notice the `#hero` in the `NgFor` double-quoted instruction.
The `#hero` is a "[template local variable](./template-syntax.html#local-vars")" *declaration*.
The (#) prefix declares a local variable name named `hero`.
@@ -133,11 +133,11 @@ figure.image-display
context for the interpolation in the double curly braces.
.l-sub-section
:markdown
:marked
We happened to give `NgFor` an array to display.
In fact, `NgFor` can repeat items for any [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)
object.
:markdown
:marked
## Register the NgFor Directive
Angular doesn't know that this template uses the `NgFor` directive.
@@ -149,20 +149,20 @@ figure.image-display
Look for it in the following:
+makeExample('displaying-data/ts/src/app/app.2.ts', 'imports')
:markdown
:marked
Second, we register `NgFor` as a directive accessible to the template by updating the
`@Component` decorator with a `directives` array property whose only item is `NgFor`:
+makeExample('displaying-data/ts/src/app/app.2.ts', 'directives')
:markdown
:marked
Now the heroes will appear in the view as an unordered list.
figure.image-display
img(src="/resources/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor")
.l-main-section
:markdown
:marked
## Creating a class for the data
We are defining our data directly inside our component.
@@ -177,7 +177,7 @@ figure.image-display
Create a new file called `hero.ts` and add the following short snippet to it.
+makeExample('displaying-data/ts/src/app/hero.ts')
:markdown
:marked
We've defined a class with a constructor and two properties: `id` and `name`.
If we are new to TypeScript, it may not look like we have properties. But we do. We're taking
@@ -186,30 +186,30 @@ figure.image-display
Consider the first parameter:
+makeExample('displaying-data/ts/src/app/hero.ts', 'id-parameter')
:markdown
:marked
That brief syntax simultaneously
* declares a constructor parameter and its type
* declare a public property of the same name
* initializes that property with the corresponding argument when we "new" an instance of the class.
.l-main-section
:markdown
:marked
## Use the Hero class
Let's redefine the heroes property in our component to return an array of these Heroes
and also set the `myHero` property with the first of these mock heroes.
+makeExample('displaying-data/ts/src/app/app.3.ts', 'heroes')
:markdown
:marked
We'll have to update the template.
At the moment it displays the entire hero object which used to be a string value.
Let's fix that so we interpolate the `hero.name` property
+makeExample('displaying-data/ts/src/app/app.3.ts', 'template')
:markdown
:marked
Our display looks the same but we know how much better it is under the hood.
.l-main-section
:markdown
:marked
## Conditional display with NgIf
Sometimes the app should display a view or a portion of a view only under prescribed circumstances.
@@ -220,38 +220,38 @@ figure.image-display
We can see it in action by adding the following paragraph at the bottom of the template:
+makeExample('displaying-data/ts/src/app/app.final.ts', 'message')
.alert.is-important
:markdown
:marked
Don't forget the leading asterisk (\*) in front of `*ng-if`. It is an essential part of the syntax.
Learn more about this and `NgIf` in the [Template Syntax](./template-syntax.html#ng-if) chapter.
:markdown
:marked
The [template expression](./template-syntax.html#template-expressions) inside the double quotes
looks much like JavaScript and it is much like JavaScript.
When the component's list of heroes has more than 3 items, Angular adds the paragraph to the DOM and the message appears.
If there were 3 or fewer items, Angular omits the the paragraph and there is no message.
.alert.is-helpful
:markdown
:marked
Angular isn't showing and hiding the message. It is adding and removing the paragraph element from the DOM.
That hardly matters here. It would matter a great deal from a performance perspective if
we were conditionally including or excluding a big chunk of HTML with many data bindings.
:markdown
:marked
As with the `NgFor`, we must add the `NgIf` directive to the component's metadata.
We should extend our `import` statement as before ...
+makeExample('displaying-data/ts/src/app/app.3.ts', 'import-ng-if')
:markdown
:marked
... and add it to the directives array:
+makeExample('displaying-data/ts/src/app/app.3.ts', 'directives')
:markdown
:marked
Try it out. We have four items in the array so the message should appear.
Delete one of the elements from the array, refresh the browser, and the message should no longer appear.
.l-main-section
:markdown
:marked
## Use the CORE_DIRECTIVES Constant
There are other core Angular directives, such as `NgClass` and `NgSwitch`,
@@ -266,15 +266,15 @@ figure.image-display
We'll revise our `import` statement one last time.
+makeExample('displaying-data/ts/src/app/app.final.ts', 'imports')
:markdown
:marked
and update the `directives` metadata
+makeExample('displaying-data/ts/src/app/app.final.ts', 'directives')
:markdown
:marked
Pro tip: we register this constant in almost every template we write.
.l-main-section
:markdown
:marked
## Summary
Now we know how to
- use **interpolation** with the double curly braces to display a component property,
@@ -291,7 +291,7 @@ figure.image-display
'app.ts, hero.ts')
.l-main-section
:markdown
:marked
## Next Steps
In addition to displaying data, most applications need to respond to user input.
Learn about that in the [User Input](./user-input.html) chapter.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -1,9 +1,9 @@
include ../../../../_includes/_util-fns
:markdown
:marked
# App Navigation with the Router
It's coming soon!
.l-main-section
:markdown
:marked
## What's not to love?
File diff suppressed because it is too large Load Diff
+68 -69
View File
@@ -1,27 +1,27 @@
include ../../../../_includes/_util-fns
:markdown
:marked
When the user clicks a link, pushes a button, or types on the keyboard
we want to know about it. These user actions all raise DOM events.
In this chapter we learn to bind to those events using the Angular Event Binding syntax.
:markdown
:marked
## Binding to User Input Events
We can listen to [any DOM event](https://developer.mozilla.org/en-US/docs/Web/Events)
We can listen to [any DOM event](https://developer.mozilla.org/en-US/docs/Web/Events)
with an [Angular Event Binding](./template-syntax.html#event-binding).
The syntax is simple. We assign a template expression to the DOM event name, surrounded in parentheses.
The syntax is simple. We assign a template expression to the DOM event name, surrounded in parentheses.
A click Event Binding makes for a quick illustration.
+makeExample('user-input/ts/src/app/app.html', 'click-me-button')(format=".")
:markdown
:marked
The `(click)` to the left of the equal sign identifies the button's click event as the **target of the binding**.
The text within quotes on the right is the "**template expression**" in which we
respond to the click event by calling the component's `onClickMe` method. A [template expression](./template-syntax.html#template-expressions) is a subset
of JavaScript with a few added tricks.
When writing a binding we must be aware of a template expression's **execution context**.
When writing a binding we must be aware of a template expression's **execution context**.
The identifers appearing within an expression belong to a specific context object.
That object is usually the Angular component that controls the template ... which it definitely is
in this case because that snippet of HTML belongs to the following component:
@@ -29,112 +29,112 @@ include ../../../../_includes/_util-fns
These sample can be found in http://plnkr.co/edit/mr63T5
-->
+makeExample('user-input/ts/src/app/app.ts', 'click-me-component')
:markdown
The `onClickMe` in the template refers to the `onClickMe` method of the component.
:marked
The `onClickMe` in the template refers to the `onClickMe` method of the component.
When the user clicks the button, Angular calls the component's `onClickMe` method.
.l-main-section
:markdown
:marked
## Get user input from the $event object
We can bind to all kinds of events. Let's bind to the "keyup" event of an input box and replay
what the user types back onto the screen.
This time we'll both listen to an event and grab the user's input.
+makeExample('user-input/ts/src/app/app.ts', 'key-up-component')
:markdown
:marked
Angular makes an event object available in the **`$event`** variable. The user data we want is in that variable somewhere.
The shape of the `$event` object is determined by whatever raises the event.
The `keyup` event comes from the DOM so `$event` must be a [standard DOM event object](https://developer.mozilla.org/en-US/docs/Web/API/Event).
The `$event.target` gives us the
[`HTMLInputElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement) which
has a `value` property and that's where we find our user input data.
With had this in mind when we passed `$event` to our `onKey()` component method. That method extracts the user's input and
concatenates it to the previous user data that we're accumulating in the component's' `values` property.
concatenates it to the previous user data that we're accumulating in the component's' `values` property.
We then use [interpolation](./template-syntax.html#interpolation)
to display the `values` property back on screen.
Enter the letters "abc", backspace to remove them, and we should see:
code-example().
a | ab | abc | ab | a | |
figure.image-display
img(src='/resources/images/devguide/user-input/keyup1-anim.gif' alt="key up 1")
:markdown
:marked
.l-main-section
:markdown
:marked
## Get user input from a local template variable
There's another way to get the user data without the `$event` variable.
Angular has syntax feature called [**local template variables**](./template-syntax.html#local-vars).
These variables grant us direct access to an element.
We declare a local template variable by preceding an identifier with a hash/pound character (#).
Let's demonstrate with a clever keystroke loopback in a single line of template HTML.
Let's demonstrate with a clever keystroke loopback in a single line of template HTML.
We don't actually need a dedicated component to do this but we'll make one anyway.
+makeExample('user-input/ts/src/app/app.ts', 'loop-back-component')
:markdown
:marked
We've declared a template local variable named `box` on the `<input>` element.
The `box` variable is a reference to the `<input>` element itself which means we can
grab the input element's `value` and display it
The `box` variable is a reference to the `<input>` element itself which means we can
grab the input element's `value` and display it
with interpolation between `<p>` tags. The display updates as we type. *Voila!*
**This won't work at all unless we bind to an event**. Angular only updates the bindings
(and therefore the screen)
if we do something in response to asynchronous events such as keystrokes.
In this silly example we aren't really interested in the event at all.
In this silly example we aren't really interested in the event at all.
But an Event Binding requires a template expression to evaluate when the event fires.
Many things qualify as expressions, none simpler than a one-character literal
like the number zero. That's all it takes to keep Angular happy. We said it would be clever!
That local template variable is intriguing. It's clearly easer to get to the textbox with that
variable than to go through the `$event` object. Maybe we can re-write our previous
"key-up" example using the variable to acquire the user's' input. Let's give it a try.
+makeExample('user-input/ts/src/app/app.ts', 'key-up2-component')
:markdown
That sure seems easier.
:marked
That sure seems easier.
An especially nice aspect of this approach is that our component code gets clean data values from the view.
It no longer requires knowledge of the `$event` and its structure.
<a id="key-event"></a>
.l-main-section
:markdown
:marked
## Key event filtering (with `key.enter`)
Perhaps we don't care about every keystroke.
Perhaps we don't care about every keystroke.
We're only interested in the input box value when the user hits the "Enter" key. We'd like to ignore all other keys.
When we bind to the `(keyup)` event, our event handling expression hears *every key stroke*.
When we bind to the `(keyup)` event, our event handling expression hears *every key stroke*.
We could filter the keys first, examining every `$event.keyCode`, and update the `values` property only if the key is "Enter".
Angular can filter the key events for us. Angular has a special syntax for keyboard events.
We can listen for just the "Enter" key by binding to Angular's `keyup.enter` pseudo-event.
Only then do we update the component's `values` property ...
inside the event expression rather than in the component ...
Angular can filter the key events for us. Angular has a special syntax for keyboard events.
We can listen for just the "Enter" key by binding to Angular's `keyup.enter` pseudo-event.
Only then do we update the component's `values` property ...
inside the event expression rather than in the component ...
because we can ... even if it is a dubious practice.
+makeExample('user-input/ts/src/app/app.ts', 'key-up3-component')
figure.image-display
img(src='/resources/images/devguide/user-input/keyup3-anim.gif' alt="key up 3")
.l-main-section
:markdown
:marked
## On blur
Our previous example won't transfer the current state of the input box if the user mouses away and clicks
elsewhere on the page. We only update the component's `values` property when the user presses "Enter"
inside the input box.
Let's fix that by listening to the input box's blur event as well.
+makeExample('user-input/ts/src/app/app.ts', 'key-up4-component')
.l-main-section
:markdown
:marked
## Put it all together
We learned how to [display data](./displaying-data.html) in the previous chapter.
We've acquired a small arsenal of event binding techniques in this chapter.
We've acquired a small arsenal of event binding techniques in this chapter.
Let's put it all together in a micro-app
that can display a list of heroes and add new heroes to that list
by typing in the input box and hitting "Enter", clicking "Add", or clicking
@@ -142,24 +142,24 @@ figure.image-display
figure.image-display
img(src='/resources/images/devguide/user-input/little-tour-anim.gif' alt="Little Tour of Heroes")
:markdown
Below is the entire "Little Tour of Heroes" micro-app in a single component.
:marked
Below is the entire "Little Tour of Heroes" micro-app in a single component.
We'll call out the highlights after we bask briefly in its minimalist glory.
<!--
This example in http://plnkr.co/edit/JWeIqq
-->
+makeExample('user-input/ts/src/app/app.ts', 'little-tour-of-heroes-app')
:markdown
+makeExample('user-input/ts/src/app/app.ts', 'little-tour-of-heroes-app')
:marked
We've seen almost everything here before. A few things are new or bear repeating.
### **Beware of camelCase variable names**
We enter new hero names in the `<input>` element so we chose `newHero` to be the name of the local template variable.
Unfortunately, we can't use that name when we declare the variable with (#).
Unfortunately, we can't use that name when we declare the variable with (#).
The browser forces all attribute and element names to lowercase, turning what would be `#newHero`
into `#newhero` (all lowercase). We don't want a `newhero` variable name in our template expressions.
The Angular workaround is to spell the declaration in "snake case". Angular translates "#new-hero"
into `#newhero` (all lowercase). We don't want a `newhero` variable name in our template expressions.
The Angular workaround is to spell the declaration in "snake case". Angular translates "#new-hero"
to `newHero` for template expressions ... which is exactly what we want.
### **newHero refers to the `<input>` element**
@@ -171,27 +171,26 @@ figure.image-display
Ready access to the `<input>` element also makes it easy for the `addHero` method
to clear the textbox after processing the new hero.
### **The *ng-for repeater**
The `ng-for` directive repeats the template as many times as there are heroes in the `heroes` list.
We must remember to list `NgFor` among the directives used by the component's template
We must remember to list `NgFor` among the directives used by the component's template
by importing the `CORE_DIRECTIVES` constant and adding it to the
@Component decorator's `directives` array.
We learned about `NgFor` in the "[Displaying Data](./displaying-data.html)" chapter.
We learned about `NgFor` in the "[Displaying Data](./displaying-data.html)" chapter.
.l-main-section
:markdown
:marked
## Next Steps
We've mastered the basic primitives for responding to user input and gestures.
As powerful as these primitives are, they are a bit clumsy for handling
large amounts of user input. We're operating down at the low level of events when
we should be writing two-way bindings between data entry fields and model properties.
Angular has a two-way binding called `NgModel` and we learn about it
in the `Forms` chapter.
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,6 @@
include ../../../../_includes/_util-fns
:markdown
:marked
We’ll need an Angular application to test, one as simple as possible while having all the angular features we want to test.
<!-- TODO We have such an app that you can download [here](./#). -->It’s a one-screen variation on the “Tour of Heroes” that should be familiar to you as a reader of this Developers Guide.
@@ -11,7 +11,7 @@ figure.image-display
img(src='/resources/images/devguide/application-under-test/bongos-heroes.png'
style="width:400px;" alt="Bongo's Heroes")
:markdown
:marked
At the top is a master list of heroes; at the bottom the detail for the current hero. Click a hero in the list to change the current hero. Change the name in the textbox and that name updates everywhere. The *Update* button modifies the `Hero.name` in an arbitrary way and that change also propagates everywhere on screen. The *Delete* button deletes the hero from the list and a new hero becomes current. *Refresh* clears both the list and detail, then restores the original list of heroes.
<!-- TODO You can see a short video of the app in action [here](./#) -->
@@ -33,6 +33,6 @@ figure.image-display
We’ll examine the implementation details as we evolve our tests.
.l-main-section
:markdown
:marked
## What’s Next?
Now that we’re familiar with how the test app works, we’re ready to poke at it with our first application tests written in Jasmine.
@@ -1,7 +1,7 @@
include ../../../../_includes/_util-fns
:markdown
In this chapter we’ll setup the environment for testing our sample application and write a few easy Jasmine tests of the app’s simplest parts.
:marked
In this chapter we’ll setup the environment for testing our sample application and write a few easy Jasmine tests of the app’s simplest parts.
We'll learn:
- to test one of our application classes
- why we prefer our test files to be next to their corresponding source files
@@ -10,7 +10,7 @@ include ../../../../_includes/_util-fns
.callout.is-helpful
header Prior Knowledge
:markdown
:marked
The Unit Testing chapters build upon each other. We recommend reading them in order.
We're also assuming that you're already comfortable with basic Angular 2 concepts and the tools
we introduced in the [QuickStart](../quickstart.html) and
@@ -18,7 +18,7 @@ include ../../../../_includes/_util-fns
such as <code>npm</code>, <code>gulp</code>, and <code>live-server</code>.
.l-main-section
:markdown
:marked
## Create the test-runner HTML
Step away from the Jasmine 101 folder and turn to the root folder of the application that we downloaded in the previous chapter.
@@ -47,7 +47,7 @@ include ../../../../_includes/_util-fns
We’re picking up right where we left off. All we’ve done is change the title.
.l-main-section
:markdown
:marked
## Update `package.json` for testing
We’ll assume that the application has `package.json` file that looks more or less like
@@ -61,7 +61,7 @@ pre.prettyprint.lang-bash
.alert.is-important Be sure to install <code>jasmine-core</code> , not <code>jasmine</code>!
:markdown
:marked
Let’s make one more change to the `package.json` script commands.
**Open the `package.json` ** and scroll to the `scripts` node. Look for the command named `test`. Change it to:
@@ -71,7 +71,7 @@ pre.prettyprint.lang-bash
That command will launch `live-server` and open a browser to the `unit-tests.html` page we just wrote.
.l-main-section
:markdown
:marked
## First app tests
Believe it or not … we could start testing *some* of our app right away. For example, we can test the `Hero` class:
@@ -135,7 +135,7 @@ pre.prettyprint.lang-bash
The description should be sufficient to identify the tested application part and its source file. Almost any convention will do as long as you and your team follow it consistently and are never confused.
.l-main-section
:markdown
:marked
## Run the tests
Open one terminal window and run the watching compiler command: `npm run tsc`
@@ -149,7 +149,7 @@ figure.image-display
.l-main-section
:markdown
:marked
## Critique
Is this `Hero` class even worth testing? It’s essentially a property bag with almost no logic. Maybe we should have tested the cloning feature. Maybe we should have tested id generation. We didn’t bother because there wasn’t much to learn by doing that.
@@ -161,7 +161,7 @@ figure.image-display
We need to relocate these tests to a separate file. Let’s do that next.
.l-main-section
:markdown
:marked
## Where do tests go?
Some people like to keep their tests in a `tests` folder parallel to the application source folder.
@@ -178,7 +178,7 @@ figure.image-display
You may put your tests elsewhere if you wish. We’re putting ours inside the app, next to the source files that they test.
.l-main-section
:markdown
:marked
## First spec file
**Create** a new file, ** `hero.spec.ts` ** in `src/app` next to `hero.ts`.
@@ -187,7 +187,7 @@ figure.image-display
.alert.is-important All of our unit test files follow this .spec naming pattern.
:markdown
:marked
Move the tests we just wrote in`unit-tests.html` to `hero.spec.ts` and convert them from JavaScript into TypeScript:
```
@@ -230,26 +230,26 @@ figure.image-display
figure.image-display
img(src='/resources/images/devguide/first-app-tests/Jasmine-not-running-tests.png' style="width:400px;" alt="Jasmine not running any tests")
:markdown
:marked
That’s Jasmine saying “**things are _so_ bad that _I’m not running any tests_.**”
Open the browser’s Developer Tools (F12, Ctrl-Shift-i). There’s an error:
code-example(format="" language="html").
Uncaught ReferenceError: exports is not defined
.l-main-section
:markdown
:marked
## Load tests with systemjs
The immediate cause of the error is the `export` statement in `hero.ts`.
That error was there all along.
The immediate cause of the error is the `export` statement in `hero.ts`.
That error was there all along.
It wasn’t a problem until we tried to `import` the `Hero` class in our tests.
Our test environment lacks support for module loading.
Our test environment lacks support for module loading.
Apparently we can’t simply load our application and test scripts like we do with 3rd party JavaScript libraries.
We are committed to module loading in our application.
We are committed to module loading in our application.
Our app will call `import`. Our tests must do so too.
We add module loading support in four steps:
@@ -296,39 +296,39 @@ figure.image-display
.l-main-section
:markdown
:marked
## Observations
### System.config
System.js demands that we specify a default extension for the filenames that correspond to whatever it is asked to import.
System.js demands that we specify a default extension for the filenames that correspond to whatever it is asked to import.
Without that default, it would translate an import statement such as `import {Hero} from ‘./here’` to a request for the file named `hero`.
Not `hero.js`. Just plain `hero`. Our server error with “404 - not found” because it doesn’t have a file of that name.
Once configured with a default extension of ‘js’,&nbsp; Systemjs requests `hero.js` which *does* exist and is promptly returned by our server.
### Asynchronous System.import
The call to `System.import` shouldn’t surprise us but it’s asynchronous nature might.
If we ponder this for a moment, we realize that it must be asynchronous because
The call to `System.import` shouldn’t surprise us but it’s asynchronous nature might.
If we ponder this for a moment, we realize that it must be asynchronous because
System.js may have to fetch the corresponding JavaScript file from the server.
Accordingly, `System.import` returns a promise and we must wait for that promise to resolve.
Accordingly, `System.import` returns a promise and we must wait for that promise to resolve.
Only then can Jasmine start evaluating the imported tests.
### window.onload
Jasmine doesn’t have a `start` method. It wires its own start to the browser window’s `load` event.
That makes sense if we’re loading our tests with script tags.
Jasmine doesn’t have a `start` method. It wires its own start to the browser window’s `load` event.
That makes sense if we’re loading our tests with script tags.
The browser raises the `load` event when it finishes loading all scripts.
But we’re not loading test scripts inline anymore.
We’re using the systemjs module loader and it won’t be done until long after the browser raised the `load` event.
But we’re not loading test scripts inline anymore.
We’re using the systemjs module loader and it won’t be done until long after the browser raised the `load` event.
Meanwhile, Jasmine started and ran to completion … with no tests to evaluate … before the import completed.
So we must wait until the import completes and only then call the window `onLoad` handler.
So we must wait until the import completes and only then call the window `onLoad` handler.
Jasmine re-starts, this time with our imported test queued up.
.l-main-section
:markdown
:marked
## What’s Next?
We are able to test a part of our application with simple Jasmine tests.
We are able to test a part of our application with simple Jasmine tests.
The part was a stand-alone class that made no mention or use of Angular.
That’s not rare but it’s not typical either. Most of our application parts make some use of the Angular framework.
+4 -4
View File
@@ -1,6 +1,6 @@
include ../../../../_includes/_util-fns
:markdown
:marked
We write **unit tests** to explore and confirm the **behavior** of parts of our application.
We like *having* unit tests for many reasons, three of them in particular:
@@ -22,7 +22,7 @@ include ../../../../_includes/_util-fns
figure.image-display
img(src='/resources/images/devguide/unit-testing/spectrum.png' alt="Functional Testing Spectrum")
:markdown
:marked
<table style="box-shadow: none">
<tr>
<td style="border-bottom: none">Pure unit test</td>
@@ -113,12 +113,12 @@ figure.image-display
.callout.is-helpful
header How to Use This Guide
:markdown
:marked
The Unit Testing chapters build upon each other. We recommend reading them in order.
We're also assuming that you're already comfortable with basic Angular 2 concepts and the tools
we introduced in the [QuickStart](../quickstart.html) and
the [Tour of Heroes](../tutorial/) tutorial
such as <code>npm</code>, <code>gulp</code>, and <code>live-server</code>.
:markdown
:marked
Let’s get started!
@@ -1,6 +1,6 @@
include ../../../../_includes/_util-fns
:markdown
:marked
We’ll write our tests with the [Jasmine test framework](http://jasmine.github.io/2.3/introduction.html).
We’ll start by getting *some* tests to work - *any* tests at all.
@@ -12,7 +12,7 @@ include ../../../../_includes/_util-fns
**Create a new project folder** perhaps called `angular2-unit-testing`.
.l-main-section
:markdown
:marked
## Install npm packages locally
Next follow all of the steps prescribed in “Install npm packages locally” of the
@@ -24,19 +24,19 @@ pre.prettyprint.lang-bash
code npm install jasmine-core --save-dev --save-exact
.alert.is-important
:markdown
:marked
Be sure to install `jasmine-core` , not `jasmine`!
:markdown
:marked
**Create a sub-folder `src` ** for our tests and then **cd into it**.
We are going to **display and control our tests in the browser**.
.l-sub-section
:markdown
:marked
The browser is nice during development of a few tests. It’s not the best venue for working with a lot of tests and it won’t do at all for build automation. We’ll switch to the karma test-runner when the time comes. But the browser will do for now.
:markdown
:marked
Create a new file called`unit-tests.html` and enter the following:
```
<html>
@@ -70,20 +70,20 @@ pre.prettyprint.lang-bash
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/jasmine-1-spec-0-failures.png' style="height:170px;" alt="Jasmine HTML test output")
:markdown
:marked
It doesn’t get much simpler than that!
.l-main-section
:markdown
:marked
## First TypeScript Test
Perhaps too simple. We won’t write our entire test suite inside one HTML file.
Let’s **extract** that line of test code to a **new file in `src` called `1st.spec.ts` ** .
.l-sub-section
:markdown
:marked
Among Jasmine developers, a test is known as a “spec” and test filenames include the word “spec”. We’ll stick with that convention.
:markdown
:marked
The test we wrote is valid TypeScript because any JavaScript is valid TypeScript. But let’s make it more modern with an arrow function:
```
it('true is true', () => expect(true).toEqual(true));
@@ -97,7 +97,7 @@ figure.image-display
That’s a reminder that we need to compile our TypeScript test files as we do our TypeScript application files. Do that next.
.l-main-section
:markdown
:marked
## Prepare for TypeScript
As we’ve seen before, we first have to tell the compiler how to compile our TypeScript files with
@@ -125,12 +125,12 @@ pre.prettyprint.lang-bash
code npm run tsc
.alert.is-helpful
:markdown
:marked
Our editor and the compiler may complain that they don’t know
what `it` and `expect` are because they lack the typing files that describe Jasmine.
We can ignore those annoying complaints for now as they are harmless.
:markdown
:marked
If we reload the browser, we should see the same Jasmine test-runner output as before.
We’ll be evolving these tests rapidly and it would be nice to have the browser refresh automatically as we make changes and recompile.
@@ -140,13 +140,13 @@ pre.prettyprint.lang-bash
pre.prettyprint.lang-bash
code npm start
:markdown
:marked
Now navigate to `1st-tests.html`
We should get the same Jasmine test-runner output as before.
.l-main-section
:markdown
:marked
## Add a describe and another test
We can’t tell what file produced these test results. We only have one file at the moment but soon we’ll write more.
@@ -166,7 +166,7 @@ pre.prettyprint.lang-bash
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/test-report-1-spec-0-failures.png' style="height:100px;" alt="1 spec, 0 failures")
:markdown
:marked
Let’s add another Jasmine test to `1st.spec.ts`
```
it('null is not the same thing as undefined',
@@ -178,23 +178,23 @@ figure.image-display
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/test-report-2-specs-0-failures.png' style="height:100px;" alt="refreshed 2 specs, 0 failures")
:markdown
:marked
What does a failing test look like? Remove the `.not`. The browser refreshes and shows:
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/test-report-2-specs-1-failure.png' style="height:190px;" alt="failing test 2 specs, 1 failure")
:markdown
:marked
Click the `Spec List` link just below “2 specs, 1 failure” to see the summary again:
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/spec-list-2-specs-1-failure.png' style="height:140px;" alt="2 specs, 1 failure")
:markdown
:marked
We can re-run just the failing test by double-clicking it. Try it!
.l-main-section
:markdown
:marked
## Debug the test
Suppose we didn’t know what was going on. We can debug it in the browser.
@@ -209,7 +209,7 @@ figure.image-display
figure.image-display
img(src='/resources/images/devguide/jasmine-testing-101/null-to-equal-undefined.png' style="height:500px;" alt="null === undefined")
:markdown
:marked
How about that! They really aren’t equal.
- remove the breakpoint (right-click in the “Breakpoints” section and chose “Remove breakpoint”)
- Click the “play” icon to resume the test (or F8)
@@ -222,14 +222,14 @@ figure.image-display
<!-- TODO
.l-main-section
:markdown
:marked
## Learn more
Learn more about basic Jasmine testing here
[Resources TBD](./#)
-->
.l-main-section
:markdown
:marked
## What’s Next?
Now that we’re familiar with Jasmine on its own, we’re ready to test an application.
@@ -1,6 +1,6 @@
include ../../../../_includes/_util-fns
:markdown
:marked
We’ll test an Angular pipe in this chapter
An Angular pipe is a declarative way in HTML to transform some input into some displayable output.
@@ -12,7 +12,7 @@ include ../../../../_includes/_util-fns
code-example(format="linenums" language="html" escape="html").
<h2>{{hero.name | initCaps}} is {{userName}}'s current super hero!</h2>
:markdown
:marked
The code for `InitCapsPipe` in `init-caps-pipe.ts` is quite brief:
```
@@ -35,36 +35,36 @@ code-example(format="linenums" language="html" escape="html").
.callout.is-helpful
header Prior Knowledge
:markdown
:marked
The Unit Testing chapters build upon each other. We recommend reading them in order.
We're also assuming that you're already comfortable with basic Angular 2 concepts and the tools
we introduced in the [QuickStart](../quickstart.html) and
the [Tour of Heroes](../tutorial/) tutorial
such as <code>npm</code>, <code>gulp</code>, and <code>live-server</code>.
:markdown
:marked
## Add the Angular library
Looking back at `unit-tests.html` we realize that we have not loaded the Angular library.
Looking back at `unit-tests.html` we realize that we have not loaded the Angular library.
Yet we were able to load and test the application’s `Hero` class.
**We were lucky!** The `Hero` class has no dependence on Angular.
**We were lucky!** The `Hero` class has no dependence on Angular.
If it had depended on Angular, we’d still be staring at the Jasmine “big-time fail” screen:
figure.image-display
img(src='/resources/images/devguide/testing-an-angular-pipe/big-time-fail-screen.png'
img(src='/resources/images/devguide/testing-an-angular-pipe/big-time-fail-screen.png'
style="width:400px;" alt="Jasmine's' big time fail screen")
:markdown
If we then opened the browser’s Developer Tools (F12, Ctrl-Shift-I) and looked
in the console window, we would see that SystemJS
:marked
If we then opened the browser’s Developer Tools (F12, Ctrl-Shift-I) and looked
in the console window, we would see that SystemJS
tried to load Angular and couldn't find it.
code-example(format="" language="html" escape="html").
GET http://127.0.0.1:8080/src/angular2/angular2 404 (Not Found)
:markdown
We are writing an Angular application afterall and
we were going to need Angular sooner or later. That time has come.
GET http://127.0.0.1:8080/src/angular2/angular2 404 (Not Found)
:marked
We are writing an Angular application afterall and
we were going to need Angular sooner or later. That time has come.
The `InitCapsPiep` clearly depends on Angular as is clear in the first few lines:
```
import {Pipe} from 'angular2/angular2';
@@ -112,11 +112,11 @@ code-example(format="" language="html" escape="html").
});
});
```
Note that each test is short (one line in our case).
Note that each test is short (one line in our case).
It has a clear label that accurately describes the test. And it makes exactly one expectation.
Anyone can read these tests and understand quickly what the test does and what the pipe does.
If one of the tests fails, we know which expected behavior is no longer true.
Anyone can read these tests and understand quickly what the test does and what the pipe does.
If one of the tests fails, we know which expected behavior is no longer true.
We’ll have little trouble maintaining these tests and adding more like them as we encounter new conditions to explore.
That’s the way we like our tests!
@@ -129,10 +129,10 @@ code-example(format="" language="html" escape="html").
Hmm. We can’t just add `System.import('app/init-caps-pipe.spec')`.
The first `System.import` returns a promise as does this second import.
The first `System.import` returns a promise as does this second import.
We can’t run any of the Jasmine tests until **both imports are finished**.
Fortunately, we can create a new `Promise` that wraps both import promises and waits
Fortunately, we can create a new `Promise` that wraps both import promises and waits
for both to finish loading.
```
// #3. Import the spec files explicitly
@@ -144,13 +144,13 @@ code-example(format="" language="html" escape="html").
Try it. The browser should refresh and show
figure.image-display
img(src='/resources/images/devguide/testing-an-angular-pipe/5-specs-0-failures.png'
img(src='/resources/images/devguide/testing-an-angular-pipe/5-specs-0-failures.png'
style="width:400px;" alt="import promises 5 specs, 0 failures")
:markdown
:marked
We have a pattern for adding new tests.
In future, when we add a new spec, we add another `System.import('app/some.spec')` to
In future, when we add a new spec, we add another `System.import('app/some.spec')` to
the array argument passed to `Promise.all`.
## What’s Next?
@@ -159,5 +159,5 @@ figure.image-display
What about testing parts that *are themselves asynchronous*?
In the next chapter we’ll test a service with a public asynchronous method that fetches heroes
In the next chapter we’ll test a service with a public asynchronous method that fetches heroes
from a remote server.
+13 -13
View File
@@ -1,6 +1,6 @@
include ../../../../_includes/_util-fns
:markdown
:marked
# Tour of Heroes: the vision
Our grand plan is to build an app to help a staffing agency manage its stable of heroes.
@@ -23,9 +23,9 @@ include ../../../../_includes/_util-fns
Angular can do whatever we need it to do.
We'll be covering a lot of ground at an introductory level but we’ll find plenty of links
to chapters with greater depth.
.l-main-section
:markdown
:marked
## The End Game
Here's a visual idea of where we're going in this tour, beginning with the "Dashboard"
@@ -34,17 +34,17 @@ include ../../../../_includes/_util-fns
figure.image-display
img(src='/resources/images/devguide/toh/heroes-dashboard-1.png' alt="Output of heroes dashboard")
:markdown
:marked
Above the dashboard are two links ("Dashboard" and "Heroes").
We could click them to navigate between this Dashboard and a Heroes view.
Instead we click the dashboard hero named "Magneta" and the router takes us to a "Hero Details" view
of that hero where we can change the hero's name.
figure.image-display
img(src='/resources/images/devguide/toh/hero-details-1.png' alt="Details of hero in app")
:markdown
:marked
Links at the top can take us to either of the main views.
We'll click the "Back" button which sends us to the "Heroes" master list view with
"Magneta" as the selected hero.
@@ -52,25 +52,25 @@ figure.image-display
figure.image-display
img(src='/resources/images/devguide/toh/heroes-list-1.png' alt="Output of heroes list app")
:markdown
:marked
We click a different hero and the readonly mini-detail beneath the list reflects our new choice.
We click the "View Details" button to drill into the
editable details of our selected hero.
The following diagram captures all of our navigation options.
figure.image-display
img(src='/resources/images/devguide/toh/nav-diagram.png' alt="View navigations")
:markdown
:marked
Here's our app in action
figure.image-display
img(src='/resources/images/devguide/toh/toh-anim.gif' alt="Tour of Heroes in Action")
.l-main-section
:markdown
:marked
## How We Roll
We’ll build this Tour of Heroes together, step by step.
+27 -27
View File
@@ -1,7 +1,7 @@
include ../../../../_includes/_util-fns
.l-main-section
:markdown
:marked
# Once Upon a Time
Every story starts somewhere. Our story starts where the [QuickStart](../quickstart.html) ends.
@@ -22,32 +22,32 @@ include ../../../../_includes/_util-fns
| └── tsconfig.json
└── package.json
:markdown
:marked
### Keep the app running
Start the TypeScript compiler and have it watch for changes in one terminal window by typing
pre.prettyprint.lang-bash
code npm run tsc
:markdown
:marked
Now open another terminal window and start the server by typing
pre.prettyprint.lang-bash
code npm start
:markdown
:marked
This command starts the server, launches the app in a browser,
and keeps the app running while we continue to build the Tour of Heroes.
.l-sub-section
:markdown
:marked
These two steps watch all project files. They recompile TypeScript files and re-run
the app when any file changes.
If the watchers fail to detect renamed or new files,
stop these commands in each terminal by typing `CTRL+C` and then re-run them.
.l-main-section
:markdown
:marked
## Show our Hero
We want to display Hero data in our app
@@ -61,12 +61,12 @@ include ../../../../_includes/_util-fns
}
```
:markdown
:marked
Now we update the template in the `@Component` decoration with data bindings to these new properties.
code-example(format="").
template: '&lt;h1>{{title}}&lt/h1>&lth2>{{hero}} details!&lt/h2>'
:markdown
:marked
The browser should refresh and display our title and hero.
The double curly braces tell our app to read the `title` and `hero` properties from the component and render them.
@@ -103,7 +103,7 @@ include ../../../../_includes/_util-fns
code-example(format="").
template: '&lt;h1>{{title}}&lt/h1>&lth2>{{hero.name}} details!&lt/h2>'
:markdown
:marked
The browser refreshes and continues to display our hero’s name.
### Adding more HTML
@@ -112,7 +112,7 @@ include ../../../../_includes/_util-fns
code-example(format="linenums").
template: '&lt;h1>{{title}}&lt/h1>&lth2>{{hero.name}} details!&lt/h2>&ltdiv>&ltlabel>id: &lt/label>{{hero.id}}&lt/div>&ltdiv>&ltlabel>name: &lt/label>{{hero.name}}&lt/div>'
:markdown
:marked
Uh oh, our template string is getting long. We better take care of that to avoid the risk of making a typo in the template.
### Multi-line template strings
@@ -136,7 +136,7 @@ include ../../../../_includes/_util-fns
.callout.is-important
header A back-tick is not a single quote
:markdown
:marked
**Be careful!** A back-tick (`) looks a lot like a single quote (').
It's actually a completely different character.
Back-ticks can do more than demarcate a string.
@@ -145,7 +145,7 @@ include ../../../../_includes/_util-fns
is part of a single template string.
.l-main-section
:markdown
:marked
## Editing Our Hero
We want to be able to edit the hero name in a textbox.
@@ -161,7 +161,7 @@ include ../../../../_includes/_util-fns
&ltdiv>&ltinput value="{{hero.name}}" placeholder="name">&lt/div>
&lt/div>
`
:markdown
:marked
We see in the browser that the hero’s name does appear in the `<input>` textbox.
But something doesn’t feel right.
When we change the name, we notice that our change
@@ -175,22 +175,22 @@ include ../../../../_includes/_util-fns
In short, we want two-way data binding.
Let’s update the template to use the **`ng-model`** built-in directive for two-way binding.
.l-sub-section
:markdown
:marked
Learn more about `ng-model` in the [Template Syntax](../guide/template-syntax.html#ng-model)
:markdown
:marked
Replace the `<input>` with the following HTML
code-example(language="html").
&lt;input [(ng-model)]="hero.name" placeholder="name">
:markdown
:marked
Unfortunately, that change broke our application and we're no longer displaying the hero in the browser.
Let’s fix that next.
.l-main-section
:markdown
:marked
## Declaring Template Directives
We added the `ng-model` directive but we didn't tell Angular about it.
@@ -217,8 +217,8 @@ include ../../../../_includes/_util-fns
code-example(language="html").
EXCEPTION: No value accessor for ' ' in [null]
:markdown
:marked
Apparently declaring the `NgModel` is not quite enough.
## Declare Multiple Form Directives
@@ -234,9 +234,9 @@ include ../../../../_includes/_util-fns
bundled in a convenient array called `FORM_DIRECTIVES`.
<!-- TODO
.alert.is-helpful
:markdown
:marked
Learn more about Angular Forms in the [Forms chapter]()
:markdown
:marked
-->
Let’s forget about importing `NgModel` and import the `FORM_DIRECTIVES` array instead:
```
@@ -259,7 +259,7 @@ include ../../../../_includes/_util-fns
and plug that array into the `directives` property.
.l-main-section
:markdown
:marked
## The Road We’ve Travelled
Let’s take stock of what we’ve built.
@@ -304,12 +304,12 @@ include ../../../../_includes/_util-fns
}
bootstrap(AppComponent);
.l-main-section
:markdown
:marked
## The Road Ahead
Our Tour of Heroes only displays one hero and we really want to display a list of heroes.
We also want to allow the user to select a hero and display their details.
We’ll learn more about how to retrieve lists, bind them to the
template, and allow a user to select it in the
template, and allow a user to select it in the
[next tutorial chapter](./toh-pt2.html).
File diff suppressed because it is too large Load Diff
@@ -22,14 +22,14 @@ module.exports = function(encodeCodeBlock) {
newLines.pop();
} else {
// wierd case - first expression in str is an @example
// in this case the :markdown appear above the str passed in,
// in this case the :marked appear above the str passed in,
// so we need to put 'something' into the markdown tag.
newLines.push(sp + "."); // '.' is a dummy char
}
}
newLines.push(spMixin + line);
// after a mixin line we need to reenter markdown.
newLines.push(spMixin + ':markdown');
newLines.push(spMixin + ':marked');
isAfterMarkdownTag = true;
} else {
if ((!isAfterMarkdownTag) || (line.trim().length > 0)) {
@@ -14,7 +14,7 @@ p.location-badge.
exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }
defined in {$ githubViewLink(doc) $}
:markdown
:marked
{%- if doc.notYetDocumented %}
*Not Yet Documented*
{% else %}
@@ -43,7 +43,7 @@ p.location-badge.
pre.prettyprint
code.
{$ doc.constructorDoc.name $}{$ paramList(doc.constructorDoc.parameters) | indent(8, false) | trim $}
:markdown
:marked
{$ doc.constructorDoc.description | indentForMarkdown(6) | replace('## Example', '') | replace('# Example', '') | trimBlankLines $}
{% endif -%}
@@ -54,7 +54,7 @@ p.location-badge.
code.
{$ member.name $}{$ paramList(member.parameters) | indent(8, false) | trim $}{$ returnType(member.returnType) $}
:markdown
:marked
{$ member.description | indentForMarkdown(6) | replace('## Example', '') | replace('# Example', '') | trimBlankLines $}
{% endif %}{% endfor %}
@@ -17,7 +17,7 @@
code {$ property.bindingName | dashCase $}
| &nbsp;bound to&nbsp;
code {$ property.memberDoc.classDoc.name $}.{$ property.propertyName $}
:markdown
:marked
{$ property.memberDoc.description | indentForMarkdown(2) | trimBlankLines $}{% endfor %}
{% endif %}
@@ -29,7 +29,7 @@
code {$ property.bindingName | dashCase $}
| &nbsp;bound to&nbsp;
code {$ property.memberDoc.classDoc.name $}.{$ property.propertyName $}
:markdown
:marked
{$ event.memberDoc.description | indentForMarkdown(2) | trimBlankLines $}
{% endfor %}
{% endif %}
@@ -14,7 +14,7 @@ include ../../_util-fns
exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }
defined in {$ githubViewLink(doc) $}
:markdown
:marked
{$ doc.description | indentForMarkdown(4) | trimBlankLines $}
{% endblock %}
@@ -14,7 +14,7 @@ include ../../_util-fns
exported from {@link {$ doc.moduleDoc.id $} {$doc.moduleDoc.id $} }
defined in {$ githubViewLink(doc) $}
:markdown
:marked
{%- if doc.notYetDocumented %}
### *Not Yet Documented*
{% else %}
@@ -49,7 +49,7 @@ module.exports = function convertBackticksToCodeBlocks() {
// modulo op in next line insures that pad is always a multiple of 2 ( jade whitespace).
postPad = postPad.substr(2 + (postPad.length % 2)); // exdent
}
replaceVal = replaceVal + postPad + ':markdown\n';
replaceVal = replaceVal + postPad + ':marked\n';
}
doc.renderedContent = doc.renderedContent.replace(entireBlock, replaceVal);
captures = BACKTICK_CAPTURE.exec(doc.renderedContent);
@@ -30,7 +30,7 @@ describe('convertBackticksToCodeBlocks', function() {
'export class TypeScriptClass {\n' +
'}\n' +
'\n' +
':markdown\n' +
':marked\n' +
'postamble\n'
);
});
@@ -1,7 +1,7 @@
include ../../../../_includes/_util-fns
.l-main-section
:markdown
:marked
## Install Angular2
There are four steps to create any Angular app:
@@ -19,33 +19,33 @@ include ../../../../_includes/_util-fns
.callout.is-helpful
header Typescript vs ES5
:markdown
:marked
Although we work through the examples in TypeScript, you can also use
regular ES5. Click the ES5 link in any code box to see the ES5 JavaScript
version. Note that in ES5, you'd want to name your files `.js` rather than
`.ts`.
.l-main-section
:markdown
:marked
## Create an entry point
Create an `index.html` file and add the Angular library tags and a `main.ts` file where
you'll build your first component.
In the `<body>`, add an element called `<my-app>` that will be the root of your
application.
The TypeScript setup includes System.js, a third-party open-source library that adds ES6 module loading functionality to browsers. This step isn't needed for the ES5 version.
+makeTabs('gettingstarted', 'ts/index.html,js/index.html', 'TypeScript, JavaScript')
.callout.is-helpful
header Don't use code.angularjs.org in a live app
:markdown
:marked
This example serves the Angular library from <a href="http://code.angularjs.org">code.angularjs.org</a>. This is
fine for examples, but you'd want to serve it yourself or use a CDN for real deployment.
.l-main-section
:markdown
:marked
## Set up the starting component
In `main.ts`, create a class called `AppComponent`, configure it to bind to the
@@ -56,14 +56,14 @@ include ../../../../_includes/_util-fns
.callout.is-helpful
header Annotations vs Decorators
:markdown
:marked
If you are transpiling using a tool that translates the `@` symbols to
annotations (for example Traceur), you will need to import the annotation versions of
Component and View. That can be easily achieved using
`import {ComponentAnnotation as Component, ViewAnnotation as View}`.
.l-main-section
:markdown
:marked
## Run it!
Open `index.html` through your web server and you should see:
@@ -72,13 +72,13 @@ include ../../../../_includes/_util-fns
img(src='/resources/images/examples/setup-example1.png' alt="Example of Todo App")
.l-main-section
:markdown
:marked
## Explanations
This basic Angular app contains the structure for any app you'll build.
.l-sub-section
:markdown
:marked
### It's all a tree
You can think of Angular apps as a tree of components. This root component we've been talking about acts as the top
@@ -94,7 +94,7 @@ include ../../../../_includes/_util-fns
these in the following pages.
.l-sub-section
:markdown
:marked
### @Component and @View annotations
A component annotation describes details about the component. An annotation can be identified by its at-sign (`@`).
@@ -104,7 +104,7 @@ include ../../../../_includes/_util-fns
The `@View` annotation defines the HTML that represents the component. The component you wrote uses an inline template, but you can also have an external template. To use an external template, specify a <code>templateUrl</code> property and give it the path to the HTML file.
.l-sub-section
:markdown
:marked
### import vs. window.angular
The main difference between the ES5 and TypeScript versions is the loading of modules.

Some files were not shown because too many files have changed in this diff Show More