STEP:
Prerequist:
(1) NodeJS: download the latest version
(2) npm:?
[sudo] npm install -g npm
(3) CLI:?
[sudo] npm uninstall -g angular-cli @angular/cli
npm cache clean
[sudo] npm install -g @angular/cli
1. install the angular CLI tool
npm install -g @angular/cli
2. clean up and create a new project
ng new <project_name>
3. install bootstrap in this project
npm install --save bootstrap
.angular-cli.json
"styles" : [
? ? ? ? "../node_modules/bootstrap/dist/css/bootstrap.min.css"
? ? ? ? "styles.css"
]
4. http://localhost:4200
ng serve
如果這個(gè)port already in use:?
lost -i : 4200
kill -q <PID>
5. create a new component without create the test files
ng g c <component_name> --spec false
6. create a new sub component without create the test files
ng g c <folder_name>/<sub_folder_name> --spec false
會(huì)generate automatically a component of 3 files:
* _________.component.ts
* _________.component.html
* _________.component.css
它會(huì)自動(dòng)在app.module.ts里加這個(gè)component在@NgModule的declaration里。
Bindings
1.?
* Property Binding : [ ]
* Event Binding : ( )
* Two-way Binding : [( ?)]
2. Two-way Binding Example
server.component.ts
export class ServerComponent implements OnInit {
? ? ? ? ? ?ServerName = "initServerName";
}
server.component.html
<input type="text"
? ? ? ? ? ? class="form-control"
? ? ? ? ? ? [(ngModel)] = "serverName">
<p>{{ serverName }}</p>
FormsModule is Required for Two-way Binding!
Directives
1. output data conditionally
*ngIf
server.component.html
<p *ngIf="serverCreated; else noServer">......</p>
<ng-template #noServer>
? ? ? ? ?<p>No Server is created!</p>
</ng-template>
server.component.ts
export class ServerComponent implements OnInit {
? ? ? ? ? serverCreated = false;
? ? ? ? ? onCreateServer() {
? ? ? ? ? ? ? ? ? ? ?this.serverCreated = true;
? ? ? ? ? ?}
}
2. Outputting List by ngFor
*ngFor
server.component.html
<app-server ?*ngFor="let server of servers"></app-server>
3. Unlike structural directives, attribute directives don't add or remove elements, they only change the element they were placed on.
*ngIf --------- structural directive
*ngFor ------- Outputting List by ngFor
[ngStyle] ----- attribute directive
4. "[ ?]" wants to bind some properties to this element, this property's name happens to be "ngStyle".
[ngStyle]
server.component.html
<p ??[ngStyle] = "{backgroundColor: getColor() }"> ...... </p>
server.component.ts
getColor() {
? ? ? ?return this.serverStatus === "online" ? "green" : "red";
}
Attribute Directives VS Structural Directives
1. Attribute Directives
* Look like a normal HTML attribute (possibly with data binding or event binding)
* Only affect / change the element they are added to
2. Structural Directives
* Look like a normal HTML attribute but have a leading * (for desugaring)
* Affect a whole area in the DOM (elements get added / removed)
Example for Custom Attribute Directive
- src
? ? ?- app
? ? ? ? ? ? - basic-highlight
? ? ? ? ? ? ? ? ? ? ? ? ? - basic-highlight.directive.ts
? ? ?- app.component.css
? ? ?- app.component.html
? ? ?- app.component.ts
? ? ?- app.module.ts
1. app.component.ts
export class AppComponent {
? ? ? ? ?addNumbers = [1, 3, 5];
? ? ? ? ?evenNumbers = [2, 4];
? ? ? ? ?onlyOdd = false;
}
2. app.component.css
.container {
? ? ? ? ? margin-top : 30px;
}
.odd {
? ? ? ? ? color: red;
}
3. app.component.html
<ul ?class="list-group">
? ? ? ? <div ? *ngIf="onlyOdd">
? ? ? ? ? ? ? ? <li ? class="list-group-item"
? ? ? ? ? ? ? ? ? ? ? ?[ngClass] = "{odd: odd % 2 ! == 0}"
? ? ? ? ? ? ? ? ? ? ? ?[ngStyle] = "{background: odd % 2 !== 0? 'yellow' : 'transparent'}"
? ? ? ? ? ? ? ? ? ? ? ? *ngFor = "let odd of oddNumbers">
? ? ? ? ? ? ? ? ? ? ? {{ odd ?}} ? </li></div>
? ? ? ? ? <div *ngIf="!onlyOdd">
? ? ? ? ? ? ? ? ? <li ? class="list-group-item"
? ? ? ? ? ? ? ? ? ? ? ? ? ?[ngClass] = "{odd: even % 2 ! == 0}"
? ? ? ? ? ? ? ? ? ? ? ? ? ?[ngStyle] = "{background: even % 2 !== 0? 'yellow' : 'transparent'}"
? ? ? ? ? ? ? ? ? ? ? ? ? ?*ngFor = "let even of evenNumbers">
? ? ? ? ? ? ? ? ? ? ? ? ?{{ even }} ? ?</li></div>
<p ?appBasicHighlight>Style me with basic directive!</p>
- use "onlyOdd=false"? to make condition of *ngIf
- [ngClass] = "{odd: odd % 2 ! == 0}
* [ngClass] is for changing the attribute of .css file
* the 1st odd is the class attribute of .css file
* the 2nd odd comes from the *ngFor in the same HTML element
- <p?appBasicHighlight>
appBasicHighlight is an attribute directive like [ngStyle], [ngClass], but our custom attribute directive.
4. basic-highlighting.directive.ts
import {Directive, ElementRef, OnInit} from '@angular/core';
@Directive ({
? ? ? ? selector: '[appBasicHighLighting]'
})
export class BasicHighLightDirective implements OnInit {
? ? ? ? constructor (private elementRef : ElementRef) {}
? ? ? ? ngOnInit() {
? ? ? ? ? ? ? this.elementRef.nativeElement.style.backgroundColor = 'green';
? ? ? ? ?}
}
5. app.module.ts
import {BasicHighLightDirective} from './basic-highlight/basic-highlight.directive';
@NgModule ({
? ? ? ? ?declarations: [
? ? ? ? ? ? ? ? ? ?AppComponent,
? ? ? ? ? ? ? ? ? ?BasicHighLightDirective
? ? ? ? ?],
......
Better Example for Custom Attribute Directive ?------ Renderer
- src
? ? ? - app
? ? ? ? ? ? ?- basic-highlight
? ? ? ? ? ? ? ? ? ? ? ? ? - basic-highlight.directive.ts
? ? ? ? ? ? ?- better-highlight
? ? ? ? ? ? ? ? ? ? ? ? ? - better-highlight.directive.ts
? ? ? ?- app.component.css
? ? ? - app.component.html
? ? ? - app.component.ts
? ? ? - app.module.ts
6. better-highlight.directive.ts
import {Directive, RendererV2, OnInit, ElementRef} from '@angular/core';
@Directive ({
? ? ? ? ?selector: '[appBetterHighLight]'
})
export class BetterHighLightDirective implements OnInit {
? ? ? ? ?constructor (private elRef: ElementRef,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?private renderer: Renderer) {}
? ? ? ? ?ngOnInit() {
? ? ? ? ? ? ? ? ? ?this.renderer.setStyle( this.elRef.nativeElement,?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?'background-color', 'blue', false, false);
? ? ? ? ?}
}
-?this.renderer.setStyle( this.elRef.nativeElement,?'background-color', 'blue', false, false);
better way to manipulate DOM
Always use Renderer to manipulate DOM!
HostListener => to Listen to Host Events?
=> to react to any events?
(Host: the element where the attribute directive places)
import {Directive,RendererV2, OnInit, ElementRef} from '@angular/core';
@Directive({
? ? ? ? selector:'[appBetterHighLight]'
})
export class BetterHighLightDirective implements OnInit {
? ? ? ? ?constructor (private elRef: ElementRef,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? private renderer: Renderer) {}
? ? ? ? ngOnInit() {?}
? ? ? ?@HostListener('mouseenter') mouseover(eventDate: Event) {
? ? ? ? ? ? ? this.renderer.setStyle(this.elRef.nativeElement, 'background-color', 'blue', false, false);
? ? ? ? }
? ? ? ? @HostListener('mouseleave') mouseleave(eventDate: Event) {
? ? ? ? ? ? ? ? ?this.renderer.setStyle(this.elRef.nativeElement, 'background-color', 'transparent', false, false);
? ? ? ? }
}
* dynamic way to change the background color from transparent to blue when the mouse is over and back to transparent when the mouse is left.
* @HostListener takes events name as arguments (string). These events, names are supported by DOM site.
Communication between Components with @Output & @Input
Example 01:
show Recipe if 'recipe' is selected on the menu bar, otherwise show shopping list if 'shopping-list' is selected.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@Output()
header.component -----------------------> app.component
header.component.html
<a ... (click)="onSelected('recipe')">Recipes</a>
header.component.ts
@Output() featureSelected = new EventEmitter<string>();
onSelected(feature: string) {
? ? ? ? ?this.featureSelected.emit(feature);
}
@Output() to let its parent component know this element.
app.component.html
<app-header ?(featureSelected) = "onNavigate($event)">
(featureSelected) is the event element get from its child component header.component.ts and ($event) is sent from header.component.ts (.emit).
app.component.ts
loadedFeature = 'recipe';
onNavigate(feature: string) {
? ? ? ? ?this.loadedFeature = feature;
}
app.component.html
<app-recipe ?*ngIf="loadedFeature === 'recipe'"></app-recipe>
<app-shopping-list ? *ngIf="loadedFeature === 'shopping-list'"></app-shopping-list>
Example 02:
recipe-list is composed with recipe-items. So use recipe-item to show each item of recipe-list.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?@Input()
recipe-list -----------------------> ?recipe-item
recipe-list.component.ts
recipes: Recipe[] = {
? ? ? ? ?new Recipe( ...... ),
? ? ? ? ?new Recipe( ...... )
}
recipe-list.component.html
<app-recipe-item
? ? ? ? ? ? ?*ngFor = "let recipeEl of recipes"
? ? ? ? ? ? ?[recipe] = "recipeEl">?
</app-recipe-item>
recipe-item.component.ts
@Input() ?recipe: Recipe;
recipe-item.component.html
{{ recipe.name }}
{{ recipe.description }}
Example 03:
select one item from the recipe list and show its details in recipe detail area.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @Output() ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??@Output()
recipe-item --------------->? recipe-list--------------->? recipes ----
@Input()
----------->? recipe-detail
recipe-item.component.html
<a ? ?href="#" ? (click)="onSelected()" ></a>
{{ recipe.name }} {{ ?recipe.description }}
recipe-item.component.ts
@Output recipeSelected = new EventEmitter<void>();
@Input recipe: Recipe;
onSelected() {
? ? ? ? ? this.recipeSelected.emit();
}
recipe-list.component.html
<app-recipe-item
? ? ? ? ? ? ?*ngFor="let recipeEl of recipes"
? ? ? ? ? ? ?[recipe] = "recipeEl"
? ? ? ? ? ? ?(recipeSelected) = "onRecipeSelected (recipeEl)">?
</app-recipe-item>
recipe-list.component.ts
@Output() recipeSelected = new EventEmitter<Recipe>();
onRecipeSelected (recipe: Recipe) {
? ? ? ? ?this.recipeWasSelected.emit (recipe);
}
recipes.component.html
<app-recipe-list?
(recipeWasSelected) = "selectedRecipe = $event"> </app-recipe-list>
<app-recipe-detail
*ngIf="selectedRecipe; else infoText"
[recipe] = "selectedRecipe"></app-recipe-detail>
<ng-template #infoText>
? ? ? ? ?<p>Please select a Recipe!</p>
</ng-template>
(#infoText ------ local reference)
([recipe] = "selectedRecipe" --------?property bind)
recipes.component.ts
selectedRecipe: Recipe;
recipe-detail.component.ts
@Input() ?recipe: Recipe;
recipe-detail.component.html
{{ recipe.name }}
{{ recipe.imagePath }}
@ViewChild for <input> Form Element
shopping-edit.component.html
<input #nameInput>
<input #amountInput>
<button (click)="onAddedItem()"> ?Add ? </button>
shopping-edit.component.ts
@ViewChild('nameInput') nameInputRef: ElementRef;
@ViewChild('amountInput') amountInputRef: ElementRef;
@Output() ingredientAdded = new EventEmitter<Ingredient>();
onAddItem() {
? ? ? ? ? const ingName = this.nameInputRef.nativeElementRef.value;
? ? ? ? ? const ingAmountRef = this.amountRef.nativeElementRef.value;
? ? ? ? ? const newIngredient = new Ingredient(ingName, ingAmout);
? ? ? ? ? this.ingredientAdded.emit(newIngredient);
}
shopping-list.component.html
<app-shopping-edit
? ? ? ? ? (ingredientAdded) = "onIngredientAdded($event)"> ......?
</app-shopping-edit>
shopping-list.component.ts
ingredients: Ingredient[] = [
? ? ? ? ? new Ingredient('Apple', 5),
? ? ? ? ? new Ingredient('Tomatos', 10)
];
onIngredientAdded(ingredient: Ingredient) {
? ? ? ? ? ? this.ingredients.push(ingredient);
}
Custom Attribute Directive?
Build and use a Dropdown Directive
Before:
for opening a dropdown button
recipe-detail.component.html
<div class="btn-group open> ?...... </div>
After:
use Attribute Directive (custom attribute)
{CLI}: ? ? ng g d ./shared/dropdown.directive.ts
dropdown.directive.ts
import {Directive, HostListener, HostBinding} from '@angular/core';
@Directive ({
? ? ? ? ?selector: '[appDropdown]'
})
export class DropdownDirective {
? ? ? ? ? @HostBinding('class.open') isOpen = false;
? ? ? ? ? @HostListener('click') toggleOpen() {
? ? ? ? ? ? ? ? ? ? ? ? ?this.isOpen = !this.isOpen;
? ? ? ? ? ? ?}
}
recipe-detail.component.html
<div class="btn-group appdropdown> ...... </div>
app.module.ts
@NgModule ({
? ? ? ? ?declarations: [
? ? ? ? ? ? ? ? ? DropdownDirective
? ? ? ? ?]
})