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:
@@ -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=.*&quot;)/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=.*")/g})
|
||||
|
||||
:markdown
|
||||
:marked
|
||||
A more complicated example might be:
|
||||
|
||||
code-example(format="linenums" language="js").
|
||||
- var stylePattern = { pnk: /script (src=.*&quot;)/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=.*")/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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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’, 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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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: '<h1>{{title}}</h1><h2>{{hero}} details!</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: '<h1>{{title}}</h1><h2>{{hero.name}} details!</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: '<h1>{{title}}</h1><h2>{{hero.name}} details!</h2><div><label>id: </label>{{hero.id}}</div><div><label>name: </label>{{hero.name}}</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
|
||||
<div><input value="{{hero.name}}" placeholder="name"></div>
|
||||
</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").
|
||||
<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 $}
|
||||
| bound to
|
||||
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 $}
|
||||
| bound to
|
||||
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
Reference in New Issue
Block a user