Angular Intro

@LeeGenD

基于TypeScript

 

Angular是什么

  • 一个框架
  • 让构建web应用更加简单的
  • 包含申明性模版、依赖注入等功能

Angular有什么

  • 指令、组件、数据绑定、服务、管道、模块

对比AngularJS

  • TypeScript,类型检查、方法调用等检测
  • 性能,渲染速度更快
  • 懒加载,初始资源加载少

                基础

指令(Directive)

  • 通过指令这一标记,给dom添加特定的自定义功能,如样式变化、结构调整、事件暴露等

使用方法

import {Directive} from '@angular/core';
@Directive({
	selector: 'my-directive',// 选择器名.class/[attr]/div
})
export class MyDirective { }

指令类型

  • 属性型指令:改变被选择的元素的属性(外观、行为)
  • 结构型指令:改变被选择的元素的结构(DOM布局)
  • 属性型指令:改变被选择的元素的属性
  • 结构型指令:改变被选择的元素的结构
<p glory="yellow">Knight</p>

<p glory="black">Kngiht</p>
  • 属性型指令:改变被选择的元素的属性
  • 结构型指令:改变被选择的元素的结构
<p *ngIf="true">Knight</p>

<p *ngIf="false">Kngiht</p>

Knight

 

   

  • 属性型指令:NgStyle...
  • 结构型指令:NgIf...
  • 属性型指令:NgStyle,NgClass,NgModel,Click...
  • 结构型指令:NgIf,NgFor,NgSwitch,NgTemplate...

Ng家族

组件(Component)

  • 一种特殊的指令,拥有模版和样式

使用方法

import {Component} from '@angular/core';
@Component({
	/* 和directive相同的属性 */
	selector: 'my-component',
	styles: ['p {color:white;}'],
	template: '<p>Hello World</p>'
})
export class MyComponent { }
<!-- 调用bat-man组件 -->
<bat-man></bat-man>
import {Component} from '@angular/core';
@Component({
    /* 和directive相同的属性 */
    selector: 'bat-man',
    styles: ['/** 一顿乱调 */'],
    template: `
        <div bat-suit>
            <header>Bruce Wayne</header>
            <div>body</div>
            <footer>foot</footer>
        </div>    
    `
})
export class MyComponent { }
<!-- 调用bat-man组件 -->
<bat-man></bat-man>

转换

import {Component} from '@angular/core';
@Component({
    /* 和directive相同的属性 */
    selector: 'bat-man',
    styles: ['/** 一顿乱调 */'],
    template: `
        <div bat-suit>
            <header>Bruce Wayne</header>
            <div>body</div>
            <footer>foot</footer>
        </div>    
    `
})
export class MyComponent { }
<!-- 调用bat-man组件 -->
<bat-man></bat-man>

转换

生成

加特效

模板

import {Component} from '@angular/core';
@Component({
	selector: 'my-component',
	template: `
            <h1>Secret Information</h1>
            <p *ngIf="isShow">It's *** who have done this to you!</p>
            <ng-content></ng-content> <!-- 内容投影 -->
        `,
        // templateUrl: './my-component.component.html' // 外部引用方式
})
export class MyComponent { }
  • 原生DOM
  • Angular指令
  • ng-content(内容投影)
<!-- my-component.template -->
<h1>Secret Information</h1>
<p *ngIf="isShow">It's *** who have done this to you!</p>
<ng-content></ng-content> <!-- 内容投影 -->

ng-content

<my-component>Hello World</my-component>
<!-- my-component.template最终生成 -->
<h1>Secret Information</h1>
<p *ngIf="isShow">It's *** who have done this to you!</p>
Hello World <!-- 内容投影被替换 -->

生命周期

初始化数据

构建content元素(内容投影)

构建view元素(template)

生命周期钩子

class ParentElementComponent {
    constructor() {}
    ngOnChanges(changes: any) {}// 当被绑定的输入属性(@Input)的值发生变化时调用

    ngOnInit() {}// 初始化指令/组件,仅一次(重要!)
    ngDoCheck() {}// 在每个Angular变更检测周期中调用

    ngAfterContentInit() {}// 当把内容投影进组件之后调用,仅一次
    ngAfterContentChecked() {}// 检测内容投影的变化后调用

    ngAfterViewInit() {}// 初始化完组件视图及其子视图之后调用,仅一次
    ngAfterViewChecked() {}// 检测视图及其子视图的变化后调用

    ngOnDestroy() {}// 销毁时调用
}

组件样式_添加方法

// 模板内联
@Component({
  selector: 'hero-app',
  template: `
    <h1>Tour of Heroes</h1>
    <style>
    span { border: 1px solid black;}
    </style>`
})
// 设置styles或styleUrls元数据
@Component({
  selector: 'hero-app',
  template: `
    <h1>Tour of Heroes</h1>
  `,
  styles: ['h1 { font-weight: normal; }'],
  styleUrls: ['app/style.css']
})

组件样式_作用域

  • 默认只作用到当前组件下
  • 默认不作用到外部元素和子组件下
  • 默认通过属性名的方式产生作用

组件样式_特殊选择器

  • :host 直接作用于宿主元素
  • :host-context 宿主上下文选择器
  • /deep/ 深作用,让样式可以作用于子组件
// 宿主上下文选择器
// 若宿主元素的祖先元素中存在类名为theme-light的元素,则生效
:host-context(.theme-light) h2 {
  background-color: #eef;
}
<issue-card></issue-card>

<p shiny-text>It's been a long day.</p>

<div class="image-uploader"></div>

<div *ngDouble></div>

what?

<issue-card></issue-card> <!-- Component -->

<p shiny-text>It's been a long day.</p> <!-- Directive -->

<div class="image-uploader"></div> <!-- Component -->

<div *ngDouble></div> <!-- Directive -->

what?

where?

where?

  • 复用性
  • 逻辑分离

数据绑定(Data Binding)

  • 组件内数据和模板内数据互相传播

简介

import { Component, 
    Input } from "@angular/core";

@Component({
    template: `
        <input [value]="inputVal" />
    `
})

class InputComponent {
    @Input() inputVal = 'init value';
} 

使用方法

<!-- 插值表达式 -->
<p>{{ name }}</p>

<!-- 属性绑定 -->
<input [value]="firstName">

<!-- 事件绑定 -->
<li (click)="selectHero(hero)"></li>

双向数据绑定

// 双向绑定内幕
@Component({
    template: `
        <input [value]="inputVal" (input)="valueChange($event)" />
    `
})

class InputComponent {
    inputVal = 'init value';

    valueChange($event) {
        this.inputVal = $event.target.value;
    }
} 

双向数据绑定

// 双向绑定内幕
@Component({
    template: `
        <input
          [ngModel]="inputVal"
          (ngModelChange)="inputVal=$event">
    `
})

class InputComponent {
    inputVal = 'init value';
} 

ngModel指令通过自己的输入属性ngModel和输出属性ngModelChange隐藏了那些细节。

服务(Service)

  • 一个广义范畴,包括:值、函数,或应用所需的特性
  • 具有专注的、明确的用途

使用方法

// 申明LogService服务
@Injectable()
export class LogService {
    info(): void {}
    error(): void {}
}

使用方法

// 注入LogService
import {Component} from '@angular/core';
@Component({
    template: '<p>Hello World</p>'
})
export class MyComponent {
    //1. 在构建函数中注入后,Angular会将其实例化
    constructor(private logService: LogService) {}
    //2. 调用LogService的方法
    print() {
        this.logService.info('hello world');   
    }
}

举例

// 浏览器地址相关
import { Location } from '@angular/common';

// loading框
import { LoadingController } from 'ionic-angular';

// 弹框
import { AlertController } from 'ionic-angular';

print?

export class TestService {
    private count = 0;
    add() {
        this.count++;
    }
    print() {
        console.log(`TestService print ${this.count}`);
    }
}
@Component({
    selector: 'tomato-item',
    providers: []
})
export class TomatoItemComponent {
    constructor(private test: TestService) {
        this.test.add();
        this.test.print();
    }
}
@Component({
    selector: 'tomato-item',
    providers: [TestService]
})
export class TomatoItemComponent {
    constructor(private test: TestService) {
        this.test.add();
        this.test.print();
    }
}
<tomato-item></tomato-item>
<tomato-item></tomato-item>
<tomato-item></tomato-item>

print?

export class TestService {
    private count = 0;
    add() {
        this.count++;
    }
    print() {
        console.log(`TestService print ${this.count}`);
    }
}
@Component({
    selector: 'tomato-item',
    providers: []
})
export class TomatoItemComponent {
    constructor(private test: TestService) {
        this.test.add();
        this.test.print();
    }
}
@Component({
    selector: 'tomato-item',
    providers: [TestService]
})
export class TomatoItemComponent {
    constructor(private test: TestService) {
        this.test.add();
        this.test.print();
    }
}
<tomato-item></tomato-item>
<tomato-item></tomato-item>
<tomato-item></tomato-item>

print?

@Component({
    selector: 'tomato-item',
    providers: []
})
export class TomatoItemComponent {
    constructor(private test: TestService) {
        this.test.add();
        this.test.print();
    }
}
@Component({
    selector: 'tomato-item',
    providers: [TestService]
})
export class TomatoItemComponent {
    constructor(private test: TestService) {
        this.test.add();
        this.test.print();
    }
}

第一次注入TestService时实例化,之后每次注入都会使用同一个TestService

每一次注入TestService都会产生新的实例

管道(Pipe)

  • 管道把数据作为输入,然后转换它,给出期望的输出

内置的管道

  • DatePipe、UpperCasePipe、LowerCasePipe、CurrencyPipe和PercentPipe

参数化

<!-- 这段代码运行于2017年7月30日 -->
<p>{{ now | date }}</p>

<p>{{ now | date: "MM/dd/yyyy" }}</p>

<p>{{ now | date: "MM/dd" }}</p>

07/30/2017

07/30

Jul 30, 2017 

链式

<!-- 这段代码运行于2017年7月30日 -->
<p>{{ now | date | uppercase }}</p>

Jul 30, 2017 

Sun Jul 30 2017 11:03:24...

JUL 30, 2017 

date

uppercase

模块(Module)

  • 帮你把应用组织成多个内聚的功能块

Service:公共服务

Directive:指令

Component:组件

Pipe:管道

子模块

NgModule

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
  // 引入其它子模块
  imports:      [ BrowserModule ],
  // Service提供商
  providers:    [ Logger ],
  // 声明Component,Directive,Pipe
  declarations: [ AppComponent,
                  HighLightDirective,
                  TipPipe ],
  // 导出
  exports:      [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

懒加载

import { NgModule }             from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

export const routes: Routes = [
  { path: 'crisis', loadChildren: 'app/crisis/crisis.module#CrisisModule' },
  { path: 'heroes', loadChildren: 'app/hero/hero.module#HeroModule' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

The End-Q&A

Todo

  • 顶部为添加模块,图片链接为网络链接
  • 添加条目后初始化分数为100%
  • 点击Good或Bad按钮,将操作记录下来,更新新的新鲜率

Todo

  • 新鲜率低于70%,显示红色,高于90%,显示绿色
  • 数据存储在localStorage
  • 举例:点Good3次,Bad1次后,新鲜率为75%

Angular Introduce

By leegend

Angular Introduce

  • 294