内容概要:
- Component Module
- Data Binding
- Local Reference (Template Reference variable)
- ng-content
- @Input / @Output
- Pipe
- Directives
- Decorator
- Service / Dependency Injection
- Encapsulation
- Selector
0. Module
A module is a collection of components, services directives, pipes, and so on. An Angular app will contain many modules, each dedicated to a single purpose.
Your modules declare which components can be used by components belonging to other modules, which classes will be injected by the dependency injector, and which component gets bootstrapped. Modules allow you to manage your components to bring modularity to your app.
1. Data Binding
三种形式:
- From the source-to-view
- From view-to-source
- Two-way sequence: view-to-source-to-view
Type | Synatx | Category |
---|---|---|
Interpolation Property Attribute Class Style |
{{expression}} [target]="expression" bind-target="expression" |
One-way from data source to view target |
Event | (target)="statement" on-target="statement" |
One-way From view target to data source |
Two-way | [(target)]="expression" bindon-target="expression" |
Two-way |
The target of a data-binding is something in the DOM. Depending on the binding type, the target can be a property (element, component, or directive), an event (element, component, or directive), or sometimes an attribute name.
-
HTML Attribute vs. DOM property
两者为截然不同的事物, attribute是由HTML定义的(比如name, id, type, value等),而Properties只能通过DOM节点访问,比如:
HTMLInputElement.id === 'the-input'
HTMLInputElement.type === 'text'
HTMLInputElement.value === 'Name:'
Template binding works with properties and events, not attributes. Attribute 用于初始化DOM property的值,之后则不会再发生改变。而property values则可以改变。
The following table summarizes the targets for the different binding types.
Type | Target | Example |
---|---|---|
Property | Element property Component property Directive property |
<img [src]="heroImageUrl"> <app-hero-detail [hero]="currentHero"></app-hero-detail> <div [ngClass]="{'special': isSpecial}"></div> |
Event | Element event Component event Directive event |
<button (click)="onSave()">Save</button> <app-hero-detail (deleteRequest)="deleteHero()"></app-hero-detail> <div (myClick)="clicked=$event" clickable>click me</div> |
Two-way | Event and Property | <input [(ngModel)]="name"> |
Attribute | Attribute (the exception) |
<button [attr.aria-label]="help">help</button> |
Class | class property | <div [class.special]="isSpecial">Special</div> |
Style | style property | <button [style.color]="isSpecial ? 'red' : 'green'"> |
-
Two-way binding
Two-way binding does two things:
1.Set a specific element property.
2.Listen for an element event.
Syntax: [()]
2. Local Reference(Template Reference variable)
Use the hash symbol (#) to declare a reference variable. The following reference variable, #phone, declares a phone variable on an <input> element.
<input #phone placeholder="phone number" />
<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>
We can get the local reference value in typeScript by:
@ViewChild('phone') phone;
但是不建议使用此种方法修改DOM中元素的值@ViewChild(ChildComponent) can be also used to access the child component by a parent component.
The scope of a reference variable is the entire template. So, don't define the same variable name more than once in the same template as the runtime value will be unpredictable.
3. ng-content and content projection
When sometimes you have complex HTML code, which you want to pass to a component from outside.
例子:
在app.component 中嵌套 card.component. 在cardComponent中header 与 footer 为固定模板, 而body则希望被动态添加。
In card component:
<!-- card.component.html -->
<div class="card">
<div class="card-header">
{{ header }}
</div>
<!-- single slot transclusion here -->
<!--"Replace me only if the element has card-body attribute". -->
<ng-content select="[card-body]"></ng-content>
<div class="card-footer">
{{ footer }}
</div>
</div>
In app component:
<!-- app.component.html -->
<h1>Single slot transclusion</h1>
<card header="my header" footer="my footer">
<!-- put your dynamic content here -->
<div class="card-block" card-body>
<h4 class="card-title">You can put any content here</h4>
<p class="card-text">For example this line of text and</p>
<a href="#" class="btn btn-primary">This button</a>
</div>
<!-- end dynamic content -->
<card>
-
Use @ContentChild to access ng-content
在上面的例子中,可以在card组件中访问到写在 app组件中的动态模板内容。
在app component 中定义 #headText, 尝试在card component中获得<h4></h4>标签中的文字。
In app component:
<!-- app.component.html -->
<h1>Single slot transclusion</h1>
<card header="my header" footer="my footer">
<!-- put your dynamic content here -->
<div class="card-block" card-body #cardbody>
<h4 class="card-title" #headText>You can put any content here</h4>
<p class="card-text">For example this line of text and</p>
<a href="#" class="btn btn-primary">This button</a>
</div>
<!-- end dynamic content -->
<card>
In card component typeScript file:
<!-- card.component.html -->
import {AfterContentInit, Component, ContentChild, ElementRef, Input, OnInit, ViewEncapsulation} from '@angular/core';
@Component({
selector: 'app-card',
templateUrl: './card.component.html',
styleUrls: ['./card.component.css'],
})
export class ServerElementComponent implements OnInit, AfterContentInit {
@ContentChild('headText', {static: true}) hText: ElementRef;
constructor() { }
ngOnInit(): void {
console.log('In ngOnInit --- ' + this. hText.nativeElement.textContent);
// 打印结果:
// In ngOnInit ---
}
ngAfterContentInit(): void{
console.log('In agAfterContentInit ---- ' + this. hText.nativeElement.textContent);
// 打印结果:
//In agAfterContentInit ---- You can put any content here
}
}
4. @Input(), @Output()
这两个properties用于提供父组件与嵌套的子组件之间进行传值交流。 An @Input() property is writable while an @Output() property is observable.
-
How to use @Input():
需要在child component中声明如下:
@Input() item: string
-
How to use @Output():
The child component then has to raise an event so the parent knows something has changed. To raise an event, @Output() works hand in hand with EventEmitter, which is a class in @angular/core
that you use to emit custom events.
子组件需要使用EventEmitter来发送事件,以向父组件传递消息。
示例:
在Child Component中:
//HTML Code
<h2>Child component with @Output()</h2>
<label>Add an item: <input #newItem></label>
<button (click)="addNewItem(newItem.value)">Add to partent's list</button>
//TypeScript code
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-item-output',
templateUrl: './item-output.component.html',
styleUrls: ['./item-output.component.css']
})
export class ItemOutputComponent {
@Output() newItemEvent = new EventEmitter<String>();
addNewItem(value: string){
this.newItemEvent.emit(value)
}
}
在Parent Component中:
//HTML Code
<app-item-output (newItemEvent)="addItem($event)"></app-item-output>
<h3>Parent component receiving value via @Output()</h3>
<ul>
<li *ngFor = "let item of items">{{item}}</li>
</ul>
//TypeScript Code
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
items = ['item1', 'item2', 'item3', 'item4'];
addItem(newItem){
this.items.push(newItem);
}
}
子组件发送出string类型的数值,父组件则接收到string类型数值。
页面效果:
-
$event Object:
For DOM event, $event is the MouseEvent, KeyboardEvent, or the event value of the type of wheather event you listen to.
For EventEmitter events, the emitted value is available as $event.
5. Pipe (|)
Used to transfer the data. For example, dispaly a number as a currency, change text to uppercase, or filter a list and sort it.
Some built-in pipes:
<p>Title through uppercase pipe: {{title | uppercase}}</p>
<!-- convert title to uppercase, then to lowercase -->
<p>Title through a pipe chain: {{title | uppercase | lowercase}}</p>
<!-- pipe with configuration argument => "February 25, 1980" -->
<p>Manufacture date with date format pipe: {{item.manufactureDate | date:'longDate'}}</p>
<p>Item json pipe: {{item | json}}</p>
-
Custom Pipes
- Import the transform function
- Use @Pipe() decorator, and give it a name to call in HTML template.
- Include your pipe in the declarations into ngModel in app.module.ts
// A simple shorten pipe
import {Pipe, PipeTransform} from '@angular/core';
@Pipe({
name: 'shorten'
})
export class ShortenPipe implements PipeTransform{
transform(value: any, limit: number): any {
return value.length > limit ? value.substr(0, limit) + '...' : value;
}
}
//HTML Template
<strong>{{someText | shorten: 7}}</strong>
- pure pipe and impure pipe:
- Pure pipe can only detect pure changes which includes primitive values and object reference changes. It is only called when Angular detects a changes in the values or the peremeters passed to a pipe.
- Impure pipe can detect impure changes which also covers object property values and array element changes. It is called for every change detection cycle no matter whether the value or parameter changes.
6. Decorator
There are four main types:
-
Class decorators, e.g.
@Component
and@NgModule
They allow us to tell Angular that a particular class is a component or module.
import { NgModule, Component } from '@angular/core';
@Component({
selector: 'example-component',
template: '<div>Woo a component!</div>',
})
export class ExampleComponent {
constructor() {
console.log('Hey I am a component!');
}
}
@NgModule({
imports: [],
declarations: [],
})
export class ExampleModule {
constructor() {
console.log('Hey I am a module!');
}
}
-
Property decorators for properties inside classes, e.g.
@Input
and@Output
we can simply put the@Input()
decorator above the property - which Angular’s compiler will automatically create an input binding from the property name and link them.
import { Component, Input } from '@angular/core';
@Component({
selector: 'example-component',
template: '<div>Woo a component!</div>'
})
export class ExampleComponent {
@Input()
exampleProperty: string;
}
<example-component
[exampleProperty]="exampleData">
</example-component>
-
Method decorators for methods inside classes, e.g.
@HostListener
Method decorators are very similar to property decorators but are used for methods instead. A good example of this is@HostListener
. This allows us to tell Angular that when an event on our host happens, we want the decorated method to be called with the event.
import { Component, HostListener } from '@angular/core';
@Component({
selector: 'example-component',
template: 'Woo a component!'
})
export class ExampleComponent {
@HostListener('click', ['$event'])
onHostClick(event: Event) {
// clicked, `event` available
}
}
-
Parameter decorators for parameters inside class constructors, e.g.
@Inject
Parameter decorators allow us to decorate parameters in our class constructors. An example of this is@Inject
that lets us tell Angular what we want that parameter to be initiated with:
import { Component, Inject } from '@angular/core';
import { MyService } from './my-service';
@Component({
selector: 'example-component',
template: 'Woo a component!'
})
export class ExampleComponent {
constructor(@Inject(MyService) myService) {
console.log(myService); // MyService
}
}
7. Directives
Directives are classes that add additional behavior to elements in your Angular applications.
There are three kinds of directives in Angular:
1.Components—directives with a template.
2.Structural directives—change the DOM layout by adding and removing DOM elements.
3.Attribute directives—change the appearance or behavior of an element, component, or another directive.
-
attribute directives
Attribute directives listen to and modify the behavior of other HTML elements, attributes, properties, and components. You usually apply them as if they were HTML attributes.
Some built-in attribute directives:
-
structure directives
Structural directives are responsible for HTML layout. They shape or reshape the DOM's structure, typically by adding, removing, and manipulating the host elements to which they are attached.
Some built-in structure directives:
-
Create a custom directives
By running the command: ng g d myDirective
8. Service / Dependency Injection
Dependency Injection is a design pattern.
Dependencies are services or objects that a class needs to perform its function. DI is a coding pattern in which a class asks for dependencies from external sources rather than creating them itself.
- create an injectable service class:
ng g s example
This command will created below:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root', // <--provides this service in the root ModuleInjector
})
export class ExampleService { constructor() { } }
- Add service into the provider in 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 { CompanyComponent } from './company/company.component';
import { EmployeeComponent } from './employee/employee.component';
import {LoginService} from "./login.service";
import { LoginComponent } from './login/login.component';
@NgModule({
declarations: [
AppComponent,
CompanyComponent,
EmployeeComponent,
LoginComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [LoginService],
bootstrap: [AppComponent]
})
export class AppModule { }
- Configure Injector
Provider: Providers are classes that create and manage service objects the first time that Angular needs to resolve a dependency. Providers are used to registering the classes to an angular module as a service.
Then you have to configure an Angular dependency Injector with a provider of that service to tell Angular where to inject it.
Injecror 负责创建service实例, provider 负责告知Injector如何创建service.
You can configure injectors with providers at different levels of your app, by setting a metadata value in one of three places:
In the @Injectable()decorator for the service itself.
In the @NgModule() decorator for an NgModule.
In the @Component() decorator for a component.
-
Injecting Service
You can tell Angular to inject a dependency in a component's constructor by specifying a constructor parameter with the dependency type.
constructor(@Inject (exampleService) private s: ExampleService)
s.property;
s.method;
9. Encapsulation
Component CSS style are encapsulated into the component's view and don't affect the rest of the application.
即任何所定义的CSS 样式只会作用于它们所属于的component中。
Shadow DOM
Each element has its own shadow DOM behiend it, where you then could assgin styles to each element. This technology is not support by all browsers. Angular emulate it as a default behavoir in view encapsulation.Change the behavior as you want
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
// warning: few browsers support shadow DOM encapsulation at this time
encapsulation: ViewEncapsulation.None // Native
})
...
If you now define any styles for this component in CSS file, they will get applied globally.
You can also assign the encapsulation to ViewEncapsulation.Native, which will display the same result as the default behavior, but only in browsers which support it.
10. Selector
What is a selector?
A selector is one of the properties we can use to configure the component. Normally, we use the selectors to identify each component uniquely into the component tree.Selector as the Element Name: This is the basic version of the selector where the selector name represents the component as the complete element.
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'This is simple component';
}
- Selector as an Attribute: Remember the attributes used with HTML element such as name, id, class, href, src, value and other different attributes. In Angular, we can also make use of a selector as an attribute.
We can use brackets [ ] along with a selector, like this.
import { Component } from '@angular/core';
@Component({
selector: '[my-app]',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
}
- Selector as a Class: So far in this guide, we have used a selector as an element and a selector as an attribute, but we can also use the selector as a class.
import { Component } from '@angular/core';
@Component({
selector: '.my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyApp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<div class = "my-app"> </div>
</body>
</html>