美文网首页
Angular 工作原理

Angular 工作原理

作者: 拾言c | 来源:发表于2020-06-14 22:07 被阅读0次

Angular 应用,由组件构成,一颗由组件构成的树。由于组件是以树形结构组织起来的,当每个组件被渲染时,都会递归地渲染下级组件

每个组件:

  1. 组件注解(@Component)
    • selector(选择器):告诉Angular要匹配哪个HTML元素
    • template(模板):用来定义视图
  2. 视图
  3. 控制器

输入输出

组件注解

Angular核心特性:输入/输出

  • 方括号 [] :传递输入。数据通过输入绑定流入你的组件
  • 圆括号 () :处理输出。事件通过输出绑定流出你的组件
@Component({
  selector: 'inventory-app',
  template: `
  <div class="inventory-app">
    <products-list 
      [productList]="products" 
      (onProductSelected)="productWasSelected($event)">
    </products-list>
  </div>
  `
})
  1. products-list 组件中,设置名为 productList 的输入。

    products :希望将输入设置为 products 表达式的值,即 InventoryApp 类中的 this.products

  2. (onProductSelected) :我们要监听的输出的名称

    productWasSelected($event) :当有新的输入时我们想要调用的方法

    $event 在此处是一个特殊的变量,用来表示输出的内容

productListonProductSelected 都是 products-list 组件中的变量

productsproductWasSelected 则是当前组件的变量\函数

产品组件列表

组件的输入

可通过 inputs 配置项来指定组件希望接收哪些参数

@Input() name:string

组件的输出

要从组件中把数据传递出去,应使用 输出绑定,写法是: (output)="action"

在视图中,可以使用 (output)="action" 语法来监听事件

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    {{value}}
    <button (click)="increase()">Increase</button>
    <button (click)="decrease()">Decrease</button>
`,
  styleUrls: ['./counter.component.css']
})
export class CounterComponent implements OnInit {
    value: number;

  constructor() { 
      this.value = 1;
  }

  ngOnInit() {
  }

  increase() {
      this.value++;
      return false;
  }

  decrease() {
      this.value--;
      return false;
  }

}

(click)mousedown 等是按钮内置事件,也可触发自定义事件

EventEmitter 事件触发器

EventEmitter 只是一个帮你实现观察者模式的对象,是一个管理一系列订阅者并向其发布事件的对象

let ee = new EventEmitter();
ee.subscribe((name:string) => console.log(`Hello ${name}`));
ee.emit("Nate");
// --> "Hello Nate"

当把一个EventEmitter 赋值给一个输出的时候,Angular会自动帮我们订阅事件

Demo:

import { Component, OnInit, Output, EventEmitter } from '@angular/core';

@Component({
    selector: 'single-component',
    styleUrls: ['./single-component.component.css'],
    template: `
    <button (click)="liked()">Like it?</button>
`
})
export class SingleComponentComponent implements OnInit {
    
    @Output() putRingOnIt: EventEmitter<string>;
    constructor() { 
        this.putRingOnIt = new EventEmitter();
    }

    ngOnInit() {
    }

    liked():void {
        this.putRingOnIt.emit("ooo");
  }
}

如果希望在一个父级组件中使用这个输出:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-club',
//   templateUrl: './club.component.html',
  styleUrls: ['./club.component.css'],
  template: `
    <div>
        <single-component
            (putRingOnIt)="ringWasReplaces($event)"
        ></single-component>
    </div>
  `
})
export class ClubComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

  ringWasReplaces(message: string) {
    console.log(`Put your hands up: ${message}`);
  }

}

inventory-app记录

1. 新建项目,及前期准备

ng new inventory-app

resource中的images文件夹复制进/assets

修改 index.html

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>ng-book 2: Inventory App</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>
  <!-- Menu Bar -->
  <div class="ui menu">
    <div class="ui container">
      <a href="#" class="header item">
        <img class="logo" src="/assets/images/ng-book-2-minibook.png">
        ng-book 2
      </a>
      <div class="header item borderless">
        <h1 class="ui header">
            Angular 2 Inventory App
        </h1>
      </div>
    </div>
  </div>

  <div class="ui main text container">
    <app-root>Loading </app-root> <!-- <--- Our app loads here! -->
  </div>

</body>
</html>

<base href="/">

修改全局css,并将 vendor 文件夹放到 ./app/vendor 目录下

style.css

@import './app/vendor/semantic.min.css';
.ui.menu .item img.logo {
    margin-right: 1.5em;
}


.ui.items>.item {
  border: 1px solid rgba(255, 255, 255, 0.0);
  border-radius: 5px;
}

.ui.items>.item.selected {
  border: 1px solid rgba(163, 51, 200, 0.41);
  border-radius: 5px;
}

.ui.items>.item>.content>.header {
    padding-top: 1em;
}

.products-list {
  border-top: 1px solid black;
  border-left: 1px solid black;
  border-right: 1px solid black;
}

.product-row {
  border-bottom: 1px solid black;
}

.product-image {
  float: left;
}

.product-info {
  padding: 30px;
  float: left;
}

.price-display {
  padding: 30px;
  float: right;
}

.product-image {
  width: 100px;
  height: 100px;
  padding: 20px;
}

.cf:before,
.cf:after {
    content: " "; /* 1 */
    display: table; /* 2 */
}

.cf:after {
    clear: both;
}

cf {
    *zoom: 1;
}

2. 新建类 product.model

ng g class product.model

src/app/product.model.ts

export class Product {
    constructor(
        public sku:string,
        public name:string,
        public imageUrl:string,
        public department:string[],
        public price:number
    ){}
}

3. 修改app.component.ts

VSCode下格式化代码快捷键:alt+shift+F

import { Component } from '@angular/core';
import { Product } from './product.model';

@Component({
  selector: 'app-root',
//   templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  template: `
    <div class="inventory-app">
      <product-list [productList]="products"     
      (onProductSelected)="productWasSelected($event)">   
      </product-list>
    </div>
  `
})
export class AppComponent {
  products: Product[];
  constructor() {
    this.products = [
      new Product(
        'MYSHOES',
        'Black Running Shoes',
        '../assets/images/products/black-shoes.jpg',
        ['Men', 'Shoes', 'Running Shoes'],
        109.99),
      new Product(
        'NEATOJACKET',
        'Blue Jacket',
        '../assets/images/products/blue-jacket.jpg',
        ['Women', 'Apparel', 'Jackets & Vests'],
        238.99),
      new Product(
        'NICEHAT',
        'A Nice Black Hat',
        '../assets/images/products/black-hat.jpg',
        ['Men', 'Accessories', 'Hats'],
        29.99)
    ];
  }

  productWasSelected(product:Product){
    console.log('Product clicked:',product);
  }
}

4. 创建组件product-list.component.ts

ng g component 创建组件的好处是,会自动会在app.module.tsimport新创建的组件,并在declarations中引入

要使用在Angular中创建的新组建,它们必须对于当前模块是可访问的,也即若要在 InventoryApptemplate中通过 product-list 标签使用 ProductList 组件的话,就要保证 InventoryApp 满足以下两个条件之一:

  • ProductList 组件在同一模块中
  • InventoryApp 所在的模块导入(import)了 ProductList 所在的模块

在这个例子里,我们将所有的组件都放在了同一模块里,它们彼此之间都是“可见”的

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { Product } from '../product.model';

@Component({
    selector: 'product-list',
    // templateUrl: './product-list.component.html',
    template: `<div class="ui items">
    <product-row
      *ngFor="let myProduct of productList"
      [product]="myProduct"
      (click)="clicked(myProduct)"
      [class.selected]="isSelected(myProduct)">
    </product-row>
  </div>`,
    styleUrls: ['./product-list.component.css']
})
export class ProductListComponent implements OnInit {
    /*
    如果Input要使用别名可以这样定义 @Input('shortName') name:string;
    则父组件使用 <parent-component [shortName]="myName"></parent-component>
    */
    @Input() productList: Product[];
    @Output() onProductSelected: EventEmitter<Product>;

    private currentProduct: Product;
    constructor() { 
        this.onProductSelected = new EventEmitter();
    }

    ngOnInit() {
    }
    clicked(product: Product) {
        this.currentProduct = product;
        this.onProductSelected.emit(product);
    }
    isSelected(product:Product){
        if(!product || !this.currentProduct) return false;
        return product.sku===this.currentProduct.sku;
    }
}

[class.selected]="isSelected(myProduct)"

5、创建组件product-row.component.ts

import { Component, OnInit, Input, HostBinding } from '@angular/core';
import { Product } from '../product.model';

@Component({
    selector: 'product-row',
    // templateUrl: './product-row.component.html',
    styleUrls: ['./product-row.component.css'],
    template:`
        <product-image [productImg]="product"></product-image>
        <div class="content">
            <div class="header">{{ product.name }}</div>
            <div class="meta">
                <div>SKU #{{ product.sku }}</div>
            </div>
            <div class="discription">
                <product-department [productDep]="product"></product-department>
            </div>
        </div>
        <price-display [price]="product.price"></price-display>
    `
})
export class ProductRowComponent implements OnInit {
    @Input() product: Product;
    @HostBinding('attr.class') cssClass="item";
    constructor() { }

    ngOnInit() {
    }

}

@HostBinding('attr.class') cssClass="item";

6、创建 组件product-image.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { Product } from '../product.model';

@Component({
    selector: 'product-image',
    // templateUrl: './product-image.component.html',
    styleUrls: ['./product-image.component.css'],
    template: `
        <img class="product-image" [src]="productImg.imageUrl">
    `
})
export class ProductImageComponent implements OnInit {
    @Input() productImg: Product;
    constructor() { }

    ngOnInit() {
    }

}

图片处,本可以这样写:

<img src="{{productImg.imageUrl}}">

但这样写是错的

为什么呢?因为如果浏览器在Angular运行起来之前就加载了这段模板,那么会尝试以字符串{{productImg.imageUrl}} 为url来加载图片,会得到 “404 not found” 的错误,Angular 运行起来之前,浏览器会在页面上显示一个破损的图像

通过 [src] 元素属性,告诉 Angular 我们希望使用 img 标签的 [src] 输入,一旦表达式解析完成,Angular 就会把 src 元素属性替换为表达式的值

7、创建组件 price-display.component.ts

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

@Component({
    selector: 'price-display',
    // templateUrl: './price-display.component.html',
    styleUrls: ['./price-display.component.css'],
    template:`
    <!-- 在模板字符串中,$属于模板变量的特殊语法,在模板中出现$写法时注意要转义 -->
    <div class="price-display">\${{price}}</div>
  `
})
export class PriceDisplayComponent implements OnInit {
    @Input() price;
    constructor() { }

    ngOnInit() {
    }

}

8、创建组件product-department.component.ts

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

@Component({
    selector: 'product-department',
    // templateUrl: './product-department.component.html',
    styleUrls: ['./product-department.component.css'],
    template: `
    <div class="product-department">
        <!-- ngFor 迭代 productDep.department 中的每个分类,并赋值给 name -->
        <!-- let i=index 是 ngFor 中取得迭代序号的方法-->
      <span *ngFor="let name of productDep.department; let i=index">
        <a href="#">{{name}}</a>
        <!-- 三元运算符判断是否为最后一级分类 -->
        <span>{{i<(productDep.department.length-1)?'>':''}}</span>
      </span>
    </div>
  `
})
export class ProductDepartmentComponent implements OnInit {
    @Input() productDep;
    constructor() { }

    ngOnInit() {
    }

}

完成

ng05.png

数据架构

数据架构管理方面只有两个主要流派

  • 使用基于观察者模式的架构,如RxJS
  • 使用基于Flux的架构

相关文章

网友评论

      本文标题:Angular 工作原理

      本文链接:https://www.haomeiwen.com/subject/juzfxktx.html