Angular_01_入门_上篇_开发环境_CLI工具_Component

最终效果如下:


附录:

github地址: https://github.com/ixixii/angular_tutorial_demo.git

cd /Users/beyond/sg_angular/angular_01/angular-tutorial-demo

git status 

git add src/app/

git commit -m 'first commit'

git push https://github.com/ixixii/angular_tutorial_demo.git master 


.
















Angular2官方文档地址:  https://angular.io/tutorial/toh-pt1

由于内容太长,分为上中下三篇



QuickStart

Good tools make application development quicker and easier to maintain than if you did everything by hand.

The Angular CLI is a command line interface tool that can create a project, add files, 

and perform a variety of ongoing development tasks such as testing, bundling, and deployment.


The goal in this guide is to build and run a simple Angular application in TypeScript, 

using the Angular CLI while adhering to the Style Guide recommendations that benefit every Angular project.


By the end of the chapter, you'll have a basic understanding of development with the CLI 

and a foundation for both these documentation samples and for real world applications.


And you can also download the example.


Step 1. Set up the Development Environment 

You need to set up your development environment before you can do anything.

注意:  Install Node.js® and npm if they are not already on your machine.

Verify that you are running at least Node.js version 8.x or greater and npm version 5.x or greater by running node -v and npm -v in a terminal/console window. 

因为: Older versions produce errors, but newer versions are fine.


Then install the Angular CLI globally.

content_copysudo npm install -g @angular/cli



Step 2. Create a new project 

Open a terminal window.

Generate a new project and default app by running the following command:

content_copyng new my-app

如图所示: asdf


The Angular CLI installs the necessary npm packages, creates the project files, and populates the project with a simple default app. 

This can take some time.


You can add pre-packaged functionality to a new project by using the ng add command. 

The ng add command transforms a project by applying the schematics in the specified package. 

For more information, see the Angular CLI documentation.


Angular Material provides schematics for typical app layouts. 

See the Angular Material documentation for details.

Step 3: Serve the application 

Go to the project directory and launch the server.

content_copycd my-app
ng serve --open

The ng serve command launches the server, watches your files, and rebuilds the app as you make changes to those files.

Using the --open (or just -o) option will automatically open your browser on 

http://localhost:4200/.


Your app greets you with a message:



Step 4: Edit your first Angular component 

The CLI created the first Angular component for you. 

This is the root component and it is named app-root. You can find it in ./src/app/app.component.ts.


Open the component file and change the title property from 'app' to 'My First Angular App!'.

src/app/app.component.ts
content_copyexport class AppComponent {
  title = 'Hello Beyond';
}


The browser reloads automatically with the revised title. 


That's nice, but it could look better.

Open src/app/app.component.css and give the component some style.

src/app/app.component.css
content_copyh1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}


Looking good!


What's next?

That's about all you'd expect to do in a "Hello, Beyond" app.

You're ready to take the Tour of Heroes Tutorial and build a small application that demonstrates the great things you can build with Angular.

Or you can stick around a bit longer to learn about the files in your brand new project.


Project file review

An Angular CLI project is the foundation for both quick experiments and enterprise solutions.

The first file you should check out is README.md. It has some basic information on how to use CLI commands. 

Whenever you want to know more about how Angular CLI works make sure to visit the Angular CLI repository and Wiki.

Some of the generated files might be unfamiliar to you.


The src folder

Your app lives in the src folder. 

All Angular components, templates, styles, images, and anything else your app needs go here. 



???Any files outside of this folder are meant to support building your app???

src
app
app.component.css
app.component.html
app.component.spec.ts
app.component.ts
app.module.ts
assets
.gitkeep
environments
environment.prod.ts
environment.ts
browserslist
favicon.ico
index.html
karma.conf.js
main.ts
polyfills.ts
styles.css
test.ts
tsconfig.app.json
tsconfig.spec.json
tslint.json
File Purpose

app/app.component.{t

s,html,css,spec.ts}

Defines the AppComponent along with an HTML template, CSS stylesheet, and a unit test. 

It is the root component of what will become a tree of nested components as the application evolves.

app/app.module.ts

Defines AppModule, the root module that tells Angular how to assemble the application. 

Right now it declares only the AppComponent

Soon there will be more components to declare.

assets/*

A folder where you can put images and anything else to be copied wholesale when you build your application.

environments/*

This folder contains one file for each of your destination environments,

 each exporting simple configuration variables to use in your application. 


The files are replaced on-the-fly when you build your app. 

You might use a different API endpoint for development than you do for production 

or maybe different analytics tokens. 


You might even use some mock services. Either way, the CLI has you covered.

browserslist

A configuration file to share target browsers between different front-end tools.

favicon.ico

Every site wants to look good on the bookmark bar. 

Get started with your very own Angular icon.

index.html

The main HTML page that is served when someone visits your site. 

Most of the time you'll never need to edit it. 

The CLI automatically adds all js and css files when building your app 

so you never need to add any <script> or <link> tags here manually.

karma.conf.js

Unit test configuration for the Karma test runner, used when running ng test.

main.ts

The main entry point for your app. 

Compiles the application with the JIT compiler and bootstraps 

the application's root module (AppModule) to run in the browser. 


You can also use the AOT compiler without changing any code by appending the--aot flag 

to the ng build and ng serve commands.

polyfills.ts

Different browsers have different levels of support of the web standards. 

Polyfills help normalize those differences. 


You should be pretty safe with core-js and zone.js

but be sure to check out the Browser Support guide for more information.

styles.css

Your global styles go here. 

Most of the time you'll want to have local styles in your components for easier maintenance, 

but styles that affect all of your app need to be in a central place.

test.ts

This is the main entry point for your unit tests


It has some custom configuration that might be unfamiliar, 

but it's not something you'll need to edit.

tsconfig.{app|spec}.

json

TypeScript compiler configuration for the Angular app (tsconfig.app.json

and for the unit tests (tsconfig.spec.json).

tslint.json

Additional Linting configuration for TSLint together with Codelyzer, used when running ng lint

Linting helps keep your code style consistent.


The root folder

The src/ folder is just one of the items inside the project's root folder. 

Other files help you build, test, maintain, document, and deploy the app. 

These files go in the root folder next to src/.

my-app
e2e
src
app.e2e-spec.ts
app.po.ts
tsconfig.e2e.json
protractor.conf.js
node_modules/...
src/...
karma.conf.js
.editorconfig
.gitignore
angular.json
package.json
README.md
tsconfig.json
tslint.json
File Purpose

e2e/

Inside e2e/ live the end-to-end tests. 


They shouldn't be inside src/ 

because e2e tests are really a separate app that just so happens to test your main app. 


That's also why they have their own tsconfig.e2e.json.

node_modules/

Node.js creates this folder and puts all third party modules listed in package.json inside of it.

.editorconfig

Simple configuration for your editor 

to make sure everyone that uses your project has the same basic configuration. 


Most editors support an .editorconfig file. 

See http://editorconfig.org for more information.

.gitignore

Git configuration to make sure autogenerated files are not committed to source control.

angular.json

Configuration for Angular CLI. 

In this file you can set several defaults 

and also configure what files are included when your project is built. 


Check out the official documentation if you want to know more.

package.json

npm configuration listing the third party packages your project uses. 

You can also add your own custom scripts here.

protractor.conf.js

End-to-end test configuration for Protractor

used when running ng e2e.

README.md

Basic documentation for your project, pre-filled with CLI command information. 


Make sure to enhance it with project documentation 

so that anyone checking out the repo can build your app!

tsconfig.json

TypeScript compiler configuration for your IDE to pick up and give you helpful tooling.

tslint.json

Linting configuration for TSLint together with Codelyzer, used when running ng lint

Linting helps keep your code style consistent.

Next Step

If you're new to Angular, continue with the tutorial

You can skip the "Setup" step since you're already using the Angular CLI setup.





Tutorial: Tour of Heroes

The Tour of Heroes tutorial covers the fundamentals of Angular.
In this tutorial you will build an app that helps a
staffing agency (劳务中介) manage its stable of heroes.

This basic app has many of the features you'd expect to find in a data-driven application. 

1. It acquires and displays a list of heroes,

2. edits a selected hero's detail, 

3. and navigates among different views of heroic data.


By the end of the tutorial you will be able to do the following:

  • Use built-in Angular directives to show and hide elements and display lists of hero data.
  • Create Angular components to display hero details and show an array of heroes.
  • Use one-way data binding for read-only data.
  • Add editable fields to update a model with two-way data binding.
  • Bind component methods to user events, like keystrokes and clicks.
  • Enable users to select a hero from a master list and edit that hero in the details view.
  • Format data with pipes.
  • Create a shared service to assemble the heroes.
  • Use routing to navigate among different views and their components.


You'll learn enough Angular to get started and gain confidence that Angular can do whatever you need it to do.


After completing all tutorial steps, 

the final app will look like this live example / download example.



What you'll build

Here's a visual idea of where this tutorial leads, 

beginning with the "Dashboard" view and the most heroic heroes:


You can click the two links above the dashboard ("Dashboard" and "Heroes") 

to navigate between this Dashboard view and a Heroes view.


If you click the dashboard hero "Magneta," 

the router opens a "Hero Details" view where you can change the hero's name.



Clicking the "Back" button returns you to the Dashboard. 


Links at the top take you to either of the main views. 

If you click "Heroes," the app displays the "Heroes" master list view.



When you click a different hero name, 

the read-only mini detail beneath the list reflects the new choice.


You can click the "View Details" button to drill into the editable details of the selected hero.


The following diagram captures all of the navigation options.


Here's the app in action:



最终效果图:



The Application Shell

Install the Angular CLI

Install the Angular CLI, if you haven't already done so. (前面已经全局安装过了)

content_copysudo npm install -g @angular/cli


Create a new application

Create a new project named angular-tour-of-heroes with this CLI command.

content_copyng new angular-tour-of-heroes



The Angular CLI generated a new project with a default application and supporting files.

You can add pre-packaged functionality to a new project by using the ng add command. 


The ng add command transforms a project by applying the schematics in the specified package. 

For more information, see the Angular CLI documentation.


Angular Material provides schematics for typical app layouts. 

See the Angular Material documentation for details.

Serve the application

Go to the project directory and launch the application.

content_copycd angular-tour-of-heroes
ng serve --open

The ng serve command builds the app, starts the development server, watches the source files, 

and rebuilds the app as you make changes to those files.


The --open flag opens a browser to http://localhost:4200/.

You should see the app running in your browser.


Angular components

The page you see is the application shell

The shell is controlled by an Angular component named AppComponent.


Components are the fundamental building blocks of Angular applications. 

They display data on the screen, listen for user input, and take action based on that input.


Change the application title

Open the project in your favorite editor or IDE 

and navigate to the src/app folder.

You'll find the implementation of the shell AppComponent distributed over three files:

  1. app.component.ts— the component class code, written in TypeScript.
  2. app.component.html— the component template, written in HTML.
  3. app.component.css— the component's private CSS styles.



Open the component class file (app.component.ts

and change the value of the title property to 'Tour of Heroes'.

app.component.ts (class title property)
content_copytitle = 'Tour of Heroes';


Open the component template file (app.component.html

and delete the default template generated by the Angular CLI. 

Replace it with the following line of HTML.

app.component.html (template)
content_copy<h1>{{title}}</h1>


The double curly braces are Angular's interpolation binding syntax. 

This interpolation binding presents the component's title property value inside the HTML header tag.

The browser refreshes and displays the new application title.


Add application styles

Most apps strive for a consistent look across the application. 

The CLI generated an empty styles.css for this purpose. 

Put your application-wide styles there.



Here's an excerpt from the styles.css for the Tour of Heroes sample app.

src/styles.css (excerpt)
content_copy
  1. /* Application-wide Styles */
  2. h1 {
  3. color: #369;
  4. font-family: Arial, Helvetica, sans-serif;
  5. font-size: 250%;
  6. }
  7. h2, h3 {
  8. color: #444;
  9. font-family: Arial, Helvetica, sans-serif;
  10. font-weight: lighter;
  11. }
  12. body {
  13. margin: 2em;
  14. }
  15. body, input[text], button {
  16. color: #888;
  17. font-family: Cambria, Georgia;
  18. }
  19. /* everywhere else */
  20. * {
  21. font-family: Arial, Helvetica, sans-serif;
  22. }

效果如下:

Final code review

The source code for this tutorial and the complete Tour of Heroes global styles are available in the live example / download example.


Here are the code files discussed on this page





.

Summary

  • You created the initial application structure using the Angular CLI.
  • You learned that Angular components display data.
  • You used the double curly braces of interpolation to display the app title.




The Hero Editor

The application now has a basic title. 

Next you will create a new component to display hero information 

and place that component in the application shell.


Create the heroes component

Using the Angular CLI, 

generate a new component named heroes.

content_copyng generate component heroes

The CLI creates a new folder,   src/app/heroes/ 

and generates the three files of the HeroesComponent.








The HeroesComponent class file is as follows:

app/heroes/heroes.component.ts (initial version)
content_copyimport { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

You always import the Component symbol from the Angular core library 

and annotate(注解) the component class with @Component.


@Component is a decorator function(装饰器函数) that specifies the Angular metadata for the component.



The CLI generated three metadata properties:

  1. selector— the component's CSS element selector(组件的css元素选择器???)
  2. templateUrl— the location of the component's template file(模板文件).
  3. styleUrls— the location of the component's private CSS styles(私有的CSS样式).

The CSS element selector'app-heroes', matches the name of the HTML element 

that identifies this component within a parent component's template.


The ngOnInit is a lifecycle hook 

Angular calls ngOnInit shortly after creating a component. It's a good place to put initialization logic.


Always export the component class so you can import it elsewhere ... like in the AppModule.


Add a hero property

Add a hero property to the HeroesComponent for a hero named "Windstorm."

heroes.component.ts (hero property)
content_copyhero = 'あの日見た花の名前を僕達はまだ知らない';


Show the hero

Open the heroes.component.html template file. 

Delete the default text generated by the Angular CLI 

and replace it with a data binding to the new hero property.

heroes.component.html
content_copy{{hero}}


Show the HeroesComponent view

To display the HeroesComponent, you must add it to the template of the shell AppComponent.


Remember that app-heroes is the element selector for the HeroesComponent

So add an <app-heroes> element to the AppComponent template file, just below the title.

src/app/app.component.html
content_copy<h1>{{title}}</h1>
<app-heroes></app-heroes>


Assuming that the CLI ng serve command is still running, 

the browser should refresh 

and display both the application title and the hero name.

效果如下:



Create a Hero class

A real hero is more than a name.

Create a Hero class in its own file in the src/app folder. 

Give it id and name  and age properties.

src/app/hero.ts
content_copyexport class Hero {
  id: number;
  name: string;
}


Return to the HeroesComponent class 

and import the Hero class.



Refactor the component's hero property to be of type Hero

Initialize it with an id of 1 and the name "面码" and the age 15.


The revised HeroesComponent class file should look like this:

src/app/heroes/heroes.component.ts
content_copyimport { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  hero: Hero = {
    id: 1,
    name: 'Windstorm'
  };

  constructor() { }

  ngOnInit() {
  }

}


The page no longer displays properly 

because you changed the hero from a string to an object.


Show the hero object

Update the binding in the template to announce the hero's name 

and show both id and name in a details layout like this:

heroes.component.html (HeroesComponent's template)
content_copy<h2>{{ hero.name }} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div><span>name: </span>{{hero.name}}</div>

The browser refreshes and display's the hero's information.

如下所示:



Format with the UppercasePipe

Modify the hero.name binding like this.

content_copy<h2>{{ hero.name | uppercase }} 介绍</h2>

The browser refreshes 

and now the hero's name is displayed in capital(大写的) letters.


The word uppercase in the interpolation binding, 

right after the pipe operator ( | ), activates the built-in UppercasePipe.


Pipes are a good way to format strings, currency amounts, dates and other display data. 

Angular ships with several built-in pipes and you can create your own.


Edit the hero

Users should be able to edit the hero name in an <input> textbox.

The textbox should both display the hero's name property 

and update that property as the user types. 


That means data flow from the component class out to the screen 

and from the screen back to the class.


To automate that data flow, 

setup a two-way data binding between the <input> form element and the hero.name property.


Two-way binding

Refactor(重构) the details area in the HeroesComponent template 

so it looks like this:

src/app/heroes/heroes.component.html (HeroesComponent's template)
content_copy<div>
    <label>name:
      <input [(ngModel)]="hero.name" placeholder="name">
    </label>
</div>

[(ngModel)] is Angular's two-way data binding syntax.


Here it binds the hero.name property to the HTML textbox  

so that data can flow in both directions: 

from the hero.nameproperty to the textbox, 

and from the textbox back to the hero.name.


The missing FormsModule

Notice that the app stopped working when you added [(ngModel)].

To see the error, open the browser development tools and look in the console for a message like

content_copyTemplate parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'.

Although ngModel is a valid Angular directive, it isn't available by default.

It belongs to the optional FormsModule and you must opt-in to using it.


AppModule

Angular needs to know how the pieces of your application fit together 

and what other files and libraries the app requires. 

This information is called metadata


Some of the metadata is in the @Component decorators 

that you added to your component classes. 


Other critical metadata is in @NgModule decorators.


The most important @NgModule decorator  annotates  the top-level AppModule class.

The Angular CLI generated an AppModule class in  src/app/app.module.ts  when it created the project. 

This is where you opt-in to the FormsModule.


Import FormsModule

Open AppModule (app.module.ts

and import the FormsModule symbol from the @angular/forms library.

app.module.ts (FormsModule symbol import)
content_copyimport { FormsModule } from '@angular/forms'; // <-- NgModel lives here


Then add FormsModule to the @NgModule metadata's imports array, 

which contains a list of external modules that the app needs.

app.module.ts ( @NgModule imports)
content_copyimports: [
  BrowserModule,
  FormsModule
],

When the browser refreshes, the app should work again. 

You can edit the hero's name 

and see the changes reflected immediately in the <h2> above the textbox.

如下所示: 



Declare HeroesComponent

Every component must be declared in exactly one NgModule.

You didn't declare the HeroesComponent

So why did the application work?


It worked because the Angular CLI declared HeroesComponent in the AppModule when it generated that component.


Open src/app/app.module.ts 

and find HeroesComponent imported near the top.

content_copyimport { HeroesComponent } from './heroes/heroes.component';


The HeroesComponent is declared in the @NgModule.declarations array.

content_copydeclarations: [
  AppComponent,
  HeroesComponent
],


Note that AppModule declares both application components: 

AppComponent and HeroesComponent.


Final code review

Your app should look like this live example / download example

Here are the code files discussed on this page.


1. heroes.component.ts中代码如下所示


2. heroes.component.html中代码如下所示: 


3. app.module.ts中代码如下所示: 


4. app.component.ts中代码如下 所示: 


5. app.component.html中代码如下所示: 



6. hero.ts中代码如下所示: 






Summary

  • You used the CLI to create a second HeroesComponent.
  • You displayed the HeroesComponent by adding it to the AppComponent shell.
  • You applied the UppercasePipe to format the name.
  • You used two-way data binding with the ngModel directive.
  • You learned about the AppModule.
  • You imported the FormsModule in the AppModule so that Angular would recognize and apply the ngModel directive.
  • You learned the importance of declaring components in the AppModule and appreciated that the CLI declared it for you.



Display a Heroes List

In this page, you'll expand the Tour of Heroes app to display a list of heroes, 

and allow users to select a hero and display the hero's details.


Create mock heroes

You'll need some heroes to display.

Eventually you'll get them from a remote data server. 

For now, you'll create some mock heroes and pretend they came from the server.


Create a file called mock-heroes.ts in the src/app/ folder. 

Define a HEROES constant as an array of ten heroes and export it. 

The file should look like this.

src/app/mock-heroes.ts
content_copyimport { Hero } from './hero';

export const HEROES: Hero[] = [
  { id: 11, name: 'Mr. Nice' },
  { id: 12, name: 'Narco' },
  { id: 13, name: 'Bombasto' },
  { id: 14, name: 'Celeritas' },
  { id: 15, name: 'Magneta' },
  { id: 16, name: 'RubberMan' },
  { id: 17, name: 'Dynama' },
  { id: 18, name: 'Dr IQ' },
  { id: 19, name: 'Magma' },
  { id: 20, name: 'Tornado' }
];

Displaying heroes

You're about to display the list of heroes at the top of the HeroesComponent.


Open the HeroesComponent class file and import the mock HEROES.

src/app/heroes/heroes.component.ts (import HEROES)
content_copyimport { HEROES } from '../mock-heroes';


Add a heroes property to the class that exposes these heroes for binding.

content_copyheroes = HEROES;


List heroes with *ngFor

Open the HeroesComponent template file (即heroes.component.html文件)

and make the following changes:

  • Add an <h2> at the top,
  • Below it add an HTML unordered list (<ul>)
  • Insert an <li> within the <ul> that displays properties of a hero.
  • Sprinkle some CSS classes for styling (you'll add the CSS styles shortly).

Make it look like this:

heroes.component.html (heroes template)
content_copy<h2>My Heroes</h2>
<ul class="heroes">
  <li>
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>

Now change the <li> to this: (核心指令    *ngFor="let hero of heroes")

content_copy<li *ngFor="let hero of heroes">


The *ngFor is Angular's repeater directive. 

It repeats the host element for each element in a list.


In this example

  • <li> is the host element
  • heroes is the list from the HeroesComponent class.
  • hero holds the current hero object for each iteration through the list.

Don't forget the asterisk (*) in front of ngFor

It's a critical part of the syntax.

After the browser refreshes, the list of heroes appears.


Style the heroes

The heroes list should be attractive 

and should respond visually when users hover over and select a hero from the list.


In the first tutorial, you set the basic styles for the entire application in styles.css

That stylesheet didn't include styles for this list of heroes.


You could add more styles to styles.css and keep growing that stylesheet as you add components.


You may prefer instead to define private styles for a specific component 

and keep everything a component needs— the code, the HTML, and the CSS —together in one place.



This approach(方法,途经) makes it easier to re-use the component somewhere else 

and deliver the component's intended appearance even if the global styles are different.


You define private styles either inline in the @Component.styles array  

(???Excuse Me???   inline in the @Component.styles array)

or as stylesheet file(s) identified in the @Component.styleUrls array.


When the CLI generated the HeroesComponent

it created an empty heroes.component.css stylesheet for the HeroesComponent 

and pointed to it in @Component.styleUrls like this.

src/app/heroes/heroes.component.ts (@Component)
content_copy@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})


Open the heroes.component.css file and paste in the private CSS styles for the HeroesComponent


You'll find them in the final code review at the bottom of this guide.

Styles and stylesheets identified in @Component metadata are scoped to that specific component. The heroes.component.css styles apply only to the HeroesComponent and don't affect the outer HTML or the HTML in any other component.

样式代码如下:

.class_ul_heroes {
  /*background-color: #369;*/
  margin: 0 0 2em 0;
  list-style-type: none;
  padding: 0;
  width: 15em;
}

.class_ul_heroes li {
  cursor: pointer;
  /* 为子元素准备的 */
  position: relative;
  left: 0;
  background-color: #EEE;
  margin: .5em;
  padding: .3em 0;
  height: 1.6em;
  border-radius: 4px;
}

.class_ul_heroes .text {
  position: relative;
  top: -3px;
}

.class_ul_heroes .class_span_badge {
  display: inline-block;
  font-size: small;
  color: white;
  padding: 0.8em 0.7em 0 0.7em;
  background-color: #607D8B;
  line-height: 1em;
  /* ... */
  position: relative;
  left: -1px;
  top: -4px;
  height: 1.8em;
  margin-right: .8em;
  border-radius: 4px 0 0 4px;
}

.class_ul_heroes li:hover {
	color: #607D8B;
	background-color: #DDD;
	left: .1em;
}


效果如下:


Master/Detail

When the user clicks a hero in the master list, 

the component should display the selected hero's details at the bottom of the page.


In this section, you'll listen for the hero item click event and update the hero detail.


Add a click event binding

Add a click event binding to the <li> like this:  (核心代码:  (click)="onSelectFunction(hero)"  )

heroes.component.html (template excerpt)
content_copy<li *ngFor="let hero of heroes" (click)="onSelectFunction(hero)">

This is an example of Angular's event binding syntax.



The parentheses around click tell Angular to listen for the <li> element's click event. 

When the user clicks in the <li>, Angular executes the onSelectFunction(hero) expression.


onSelectFunction() is a HeroesComponent method that you're about to write. 

Angular calls it with the hero object displayed in the clicked <li>

the same hero defined previously in the *ngFor expression. (参数hero就是for循环中的hero)


Add the click event handler

Rename the component's hero property to selectedHero but don't assign it. 

Beacuse there is no selected hero when the application starts.


Add the following onSelectFunction() method, 

which assigns the clicked hero from the template to the component's selectedHero.

src/app/heroes/heroes.component.ts (onSelect)
content_copyselectedHero: Hero;

onSelectFunction(hero: Hero): void {
  this.selectedHero = hero;
}


Update the details template

The template still refers to the component's old hero property which no longer exists. 

Rename hero to selectedHero.

heroes.component.html (selected hero details)
content_copy<h2>{{ selectedHero.name | uppercase }} Details</h2>
<div><span>id: </span>{{selectedHero.id}}</div>
<div>
  <label>name:
    <input [(ngModel)]="selectedHero.name" placeholder="name">
  </label>
</div>


Hide empty details with *ngIf (条件渲染)

After the browser refreshes, the application is broken.

Open the browser developer tools and look in the console for an error message like this:

content_copyHeroesComponent.html:3 ERROR TypeError: Cannot read property 'name' of undefined

Now click one of the list items. The app seems to be working again. 

The heroes appear in a list and details about the clicked hero appear at the bottom of the page.


What happened?

When the app starts, the selectedHero is undefined by design.

Binding expressions in the template that refer to properties of selectedHero 

— expressions like {{selectedHero.name}}— must fail because there is no selected hero at all.


The fix

The component should only display the selected hero details if the selectedHero exists.


1. Wrap the hero detail HTML in a <div>

2. Add Angular's *ngIf directive to the <div> and set it to selectedHero.

Don't forget the asterisk (*) in front of ngIf

It's a critical part of the syntax.

src/app/heroes/heroes.component.html (*ngIf)
content_copy<div *ngIf="selectedHero">

  <h2>{{ selectedHero.name | uppercase }} Details</h2>
  <div><span>id: </span>{{selectedHero.id}}</div>
  <div>
    <label>name:
      <input [(ngModel)]="selectedHero.name" placeholder="name">
    </label>
  </div>

</div>

After the browser refreshes, the list of names reappears. 

The details area is blank. Click a hero and its details appear.


Why it works

When selectedHero is undefined, the ngIf removes the hero detail from the DOM. 

There are no selectedHero bindings to worry about.


When the user picks a hero, 点击事件触发后, selectedHero has a value 

and ngIf puts the hero detail into the DOM.


Style the selected hero

It's difficult to identify the selected hero in the list when all <li> elements look alike.


If the user clicks "Magneta",

 that hero should render with a distinctive but subtle background color like this:



That selected hero coloring is the work of the .selected CSS class in the styles you added earlier

You just have to apply the .selected class to the <li> when the user clicks it.


条件绑定 class样式名

The Angular class binding makes it easy to add and remove a CSS class conditionally. 

Just add [class.some-css-class-name]="some-condition" to the element you want to style.


Add the following [class.class_li_selected] binding to the <li> in the HeroesComponent template:

heroes.component.html (toggle the 'selected' CSS class)
content_copy[class.class_li_selected]="hero === selectedHero"

When the current row hero is the same as the selectedHero

Angular adds the selected CSS class. 


When the two heroes are different, Angular removes the class.


The finished <li> looks like this:

heroes.component.html (list item hero)
content_copy<li *ngFor="let hero of heroes"
  [class.selected]="hero === selectedHero"
  (click)="onSelect(hero)">
  <span class="badge">{{hero.id}}</span> {{hero.name}}
</li>

效果如下:

Final code review

Your app should look like this live example / download example.

Here are the code files discussed on this page, including the HeroesComponent styles.

src/app/heroes/heroes.component.ts 代码如下:

  1. import { Component, OnInit } from '@angular/core';
  2. import { Hero } from '../hero';
  3. import { HEROES } from '../mock-heroes';
  4.  
  5. @Component({
  6. selector: 'app-heroes',
  7. templateUrl: './heroes.component.html',
  8. styleUrls: ['./heroes.component.css']
  9. })
  10. export class HeroesComponent implements OnInit {
  11.  
  12. heroes = HEROES;
  13.  
  14. selectedHero: Hero;
  15.  
  16.  
  17. constructor() { }
  18.  
  19. ngOnInit() {
  20. }
  21.  
  22. onSelect(hero: Hero): void {
  23. this.selectedHero = hero;
  24. }
  25. }

src/app/heroes/heroes.component.html模板代码如下:



src/app/heroes/heroes.component.css样式文件代码如下:

.class_ul_heroes {
  /*background-color: #369;*/
  margin: 0 0 2em 0;
  list-style-type: none;
  padding: 0;
  width: 15em;
}

.class_ul_heroes li {
  cursor: pointer;
  /* 为子元素准备的 */
  position: relative;
  left: 0;
  background-color: #EEE;
  margin: .5em;
  padding: .3em 0;
  height: 1.6em;
  border-radius: 4px;
}

.class_ul_heroes .text {
  position: relative;
  top: -3px;
}

.class_ul_heroes .class_span_badge {
  display: inline-block;
  font-size: small;
  color: white;
  padding: 0.8em 0.7em 0 0.7em;
  background-color: #607D8B;
  line-height: 1em;
  /* ... */
  position: relative;
  left: -1px;
  top: -4px;
  height: 1.8em;
  margin-right: .8em;
  border-radius: 4px 0 0 4px;
}

.class_ul_heroes li:hover {
  color: #607D8B;
  background-color: #DDD;
  left: .1em;
}


/* 点击时选中后的 高亮效果 */
.class_li_selected {
  background-color: #CFD8DC !important;
  color: rgb(153,57,60);
}

.class_ul_heroes li.selected:hover {
  background-color: #BBD8DC !important;
  color: rgb(153,57,60);
}

 目录结构如下:


Summary

  • The Tour of Heroes app displays a list of heroes in a Master/Detail view.
  • The user can select a hero and see that hero's details.
  • You used *ngFor to display a list.
  • You used *ngIf to conditionally include or exclude a block of HTML.
  • You can toggle a CSS style class with a class binding.


Master/Detail Components

At the moment, the HeroesComponent displays both the list of heroes      and the selected hero's details.


Keeping all features in one component as the application grows will not be maintainable. 

You'll want to split up large components into smaller sub-components, 

each focused on a specific task or workflow.


In this page, you'll take the first step in that direction by moving the hero details into a separate, reusable HeroDetailComponent.

The HeroesComponent will only present the list of heroes. 

The HeroDetailComponent will present details of a selected hero.



Make the HeroDetailComponent

Use the Angular CLI to generate a new component named hero-detail.

content_copyng generate component hero-detail


The command scaffolds the HeroDetailComponent files 

and declares the component in AppModule.




Write the template

Cut the HTML for the hero detail from the bottom of the HeroesComponent template 

and paste it over the generated boilerplate in the HeroDetailComponent template.


The pasted HTML refers to a selectedHero

The new HeroDetailComponent can present any hero, not just a selected hero. 

So replace "selectedHero" with "hero" everywhere in the template.


When you're done, the HeroDetailComponent template should look like this:

src/app/hero-detail/hero-detail.component.html
content_copy<div *ngIf="hero">

  <h2>{{ hero.name | uppercase }} Details</h2>
  <div><span>id: </span>{{hero.id}}</div>
  <div>
    <label>name:
      <input [(ngModel)]="hero.name" placeholder="name"/>
    </label>
  </div>

</div>


Add the @Input() hero property

The HeroDetailComponent template binds to the component's hero property which is of type Hero.


Open the HeroDetailComponent class file and import the Hero symbol.

src/app/hero-detail/hero-detail.component.ts (import Hero)
content_copyimport { Hero } from '../hero';


HeroDeatilComponent这个子组件中的 数据源是自父类传递过来的

The hero property must be an Input property, annotated with the @Input() decorator, 

because the external HeroesComponent will bind to it like this.

在父组件的样板文件中 heros.component.html是这样给子组件传递数据hero的:

content_copy<app-hero-detail [hero]="selectedHero"></app-hero-detail>


Amend the @angular/core import statement to include the Input symbol.

// 引入input symbol,这样就可以接收父组件传递过来的数据hero了

src/app/hero-detail/hero-detail.component.ts (import Input)
content_copyimport { Component, OnInit, Input } from '@angular/core';


Add a hero property, preceded by the @Input() decorator.

@Input() 表示该属性来自父组件

content_copy@Input() hero: Hero;

That's the only change you should make to the HeroDetailComponent class. 

There are no more properties. 

There's no presentation logic. 

This component simply receives a hero object through its hero property and displays it.


Show the HeroDetailComponent

The HeroesComponent is still a master/detail view.


It used to display the hero details on its own, before you cut that portion of the template. 

Now it will delegate to the HeroDetailComponent.


The two components will have a parent/child relationship. 


The parent HeroesComponent will control the child HeroDetailComponent 

by sending it a new hero to display whenever the user selects a hero from the list.


You won't change the HeroesComponent class but you will change its template.


Update the HeroesComponent template

The HeroDetailComponent selector is 'app-hero-detail'

Add an <app-hero-detail> element near the bottom of the HeroesComponent template, 

where the hero detail view used to be.


Bind the HeroesComponent.selectedHero to the element's hero property like this.

核心代码: [hero]="selectedHero"

heroes.component.html (HeroDetail binding)
content_copy<app-hero-detail [hero]="selectedHero"></app-hero-detail>

[hero]="selectedHero" is an Angular property binding.

It's a one way data binding from the selectedHero property of the HeroesComponent 

to the hero property of the target element, which maps to the hero property of the HeroDetailComponent.


Now when the user clicks a hero in the list, the selectedHero changes. 

When the selectedHero changes, the property binding updates hero 

and the HeroDetailComponent displays the new hero.


The revised HeroesComponent template should look like this:

heroes.component.html
content_copy<h2>My Heroes</h2>

<ul class="heroes">
  <li *ngFor="let hero of heroes"
    [class.selected]="hero === selectedHero"
    (click)="onSelect(hero)">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
  </li>
</ul>

<app-hero-detail [hero]="selectedHero"></app-hero-detail>

The browser refreshes and the app starts working again as it did before.



What changed?

As before, whenever a user clicks on a hero name, the hero detail appears below the hero list. 

Now the HeroDetailComponent is presenting those details instead of the HeroesComponent.


Refactoring the original HeroesComponent into two components yields benefits(产生益处), both now and in the future:

  1. You simplified the HeroesComponent by reducing its responsibilities.

  2. You can evolve the HeroDetailComponent into a rich hero editor without touching the parent HeroesComponent.

  3. You can evolve the HeroesComponent without touching the hero detail view.

  4. You can re-use the HeroDetailComponent in the template of some future component.


Final code review

Here are the code files discussed on this page and your app should look like this live example / download example.

src/app/hero-detail/hero-detail.component.ts代码如下:

  1. import { Component, OnInit, Input } from '@angular/core';
  2. import { Hero } from '../hero';
  3.  
  4. @Component({
  5. selector: 'app-hero-detail',
  6. templateUrl: './hero-detail.component.html',
  7. styleUrls: ['./hero-detail.component.css']
  8. })
  9. export class HeroDetailComponent implements OnInit {
  10. @Input() hero: Hero;
  11.  
  12. constructor() { }
  13.  
  14. ngOnInit() {
  15. }
  16.  
  17. }


app.module.ts代码如下:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
// 表单双向绑定
import { FormsModule } from '@angular/forms';


import { AppComponent } from './app.component';
import { HeroesComponent } from './heroes/heroes.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';


@NgModule({
  declarations: [
    AppComponent,
    HeroesComponent,
    HeroDetailComponent
  ],
  imports: [
    BrowserModule,
    // 添加双向绑定所需要的模块
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }




Summary

  • 子组件(HeroDetailComponent)  You created a separate, reusable HeroDetailComponent
  • 父组件传值给子组件.  You used a property binding to give the parent HeroesComponent control over the child HeroDetailComponent.
  • You used the @Input decorator to make the hero property available for binding by the external HeroesComponent.

由于内容太长,分成上 下两部分

上部分先到这儿


下部分另写一篇




未完待续,下一章节,つづく

猜你喜欢

转载自blog.csdn.net/u012576807/article/details/80631933
今日推荐