Angular2如何运行

开发工具参考链接:https://blog.csdn.net/hdp134793/article/details/81530472

Angular2的核心思想是组件化开发,所以这篇文章以一个实例demo来解释组件之间是如何配合和工作的。

例子效果图如下:

 

 上图demo大概结构是这样子的:一整个productList是由Product类组成的数组或者Array<Product>。

Product里面由name、sku、imageUrl、department(部分、系)、price这几个属性。

由于Angular2是组件化开发,组件之间是可以组合的,并且推荐采用的方式是单向数据绑定(数据由顶层组件向下流入底层组件)。

所以可以开始规划组件之间的关系了

顶层组件InventoryApp
    |
    |
    ——产品列表组件ProList
            |
            |              
            —— 每一行的产品组件ProductRow—— |——图片组件(ProductImage
                                         |——系组件(ProductDepartment)
                                         |——价格组件(PriceDisplay)

  

第一步先创建一个应用:ng new productList。(如果卡住就加上–skip-install,然后进入目录下进行npm install)

第二步创建顶层组件InventoryApp:ng generate component InventoryApp

接着下面开始编写核心代码:

修改app.module.ts文件中bootstrap的值由AppComponent改为InventoryAppComponent。bootstrap是引导属性,设什么值就告诉NgModule将那个值作为顶层组件来引导进行加载。

然后将index.html中的<app-root>标签改为<app-inventory-app>

 

由于main.ts里面已经写了

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

 这段代码来启动项目。我们可以直接运行项目了,ng serve。

 可以显示inventory-app works!就代表顶层组件更换成功了。

 

接下来是顶层组件代码编写。

进入inventory-app.component.ts文件编写如下代码

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

@Component({
  selector: 'app-inventory-app',
  templateUrl: './inventory-app.component.html',
  styleUrls: ['./inventory-app.component.css']
})
export class InventoryAppComponent implements OnInit {
  products: Product[];

  constructor() {
    this.products = [
      new Product({
        sku: 'MYSHOES',
        name: 'Black Running Shoes',
        imageUrl: '/assets/images/products/black-shoes.jpg',
        department: ['Men', 'Shoes', 'Running Shoes'],
        price: 109.99
      }),
      new Product({
        sku: 'NEATOJACKET',
        name: 'Blue Jacket',
        imageUrl: '/assets/images/products/blue-jacket.jpg',
        department: ['Women', 'Apparel', 'Jackets & Vests'],
        price: 238.99
      }),
      new Product({
        sku: 'NICEHAT',
        name: 'A Nice Black Hat',
        imageUrl: '/assets/images/products/black-hat.jpg',
        department: ['Men', 'Accessories', 'Hats'],
        price: 29.99
      })
    ];
  }


  ngOnInit() {
  }

  /**
   * 选中产品
   * @param product
   */
  productWasSelect(product: Product): void {
    console.log('product clicked:', product);
  }
}

  

然后我们会发现Product这个类不存在,那么我们要新建一个Product这个类product.model.ts。我们称这种类文件为“模型”。

product.model.ts就放在inventory-app文件夹里面吧。

接下来编写product.model.ts这里面的代码:

export class Product {
  /**
   *
   public sku: string,
   public name: string,
   public imageUrl: string,
   public department: string[],
   public price: number
   */
  public sku: string;
  public name: string;
  public imageUrl: string;
  public department: string[];
  public price: number;

  constructor(param: any) {
    this.sku = param.sku;
    this.name = param.name;
    this.imageUrl = param.imageUrl;
    this.department = param.department;
    this.price = param.price;
  }
}

  注意:这里export关键字非常重要,不加这个关键字,虽然开发工具里面ctrl+左键点击inventory-app.component.ts里面的Product能跳转到Product文件,但是实际运行InventoryApp组件认不到这个类。

所以必须要加export关键字,然后再inventory-app.component.ts导入这个类import {Product} from './product.model'。需要停止运行的项目,鼠标移到报错的Product上面,就会有提示导入这个类。

然后编写inventory-app.component.html这个页面文件,

<div class="inventory-app">
  <app-pro-list [productList]="products" (onProductSelected)="productWasSelect($event)"></app-pro-list>
</div>

  这里需要解释下Angular2中的组件数据的输入和输出是如何运行的。

输入:

  写法用的[xxx]="yyy",xxx是组件内部定义的属性名(例子中是app-pro-list中的属性productList),yyy是输入的值(例子中来源于inventory-app.component.ts中的products属性)。

  也就是说把组件inventory-app.component中products属性的值赋值给组件app-pro-list里面某个属性,参考系是组件app-pro-list,以组件app-pro-list为观察目标,给它的某个属性赋值就是输入。

输出:

  写法用的(xxx)="yyy",onProductSelected是app-pro-list中的属性,productWasSelect是inventory-app.component.ts中的函数。

  也就是说当onProductSelected事件出发的时候,会把数据流出app-pro-list,由$event接收,传入inventory-app.component.ts中的函数productWasSelect。

  以组件app-pro-list为观察目标,数据从组件app-pro-list流出,传入组件inventory-app.component.ts,就是输出。

如此InventoryAppComponent这个组件的代码就完成了。

接下来是组件app-pro-list的pro-list.component.ts

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

@Component({
  selector: 'app-pro-list',
  templateUrl: './pro-list.component.html',
  styleUrls: ['./pro-list.component.css']
})
export class ProListComponent implements OnInit {
  @Input() productList: Product[];
  @Output() onProductSelected: EventEmitter<Product>;
  currentProduct: Product;

  constructor() {
    this.onProductSelected = new EventEmitter();
  }

  ngOnInit() {
  }

  clicked(product: Product): void {
    this.currentProduct = product;
    this.onProductSelected.emit(product);
  }

  isSelected(product: Product): boolean {
    if (!product || !this.currentProduct) {
      return false;
    }
    return product.sku === this.currentProduct.sku;
  }
}

 

这里介绍下两个注解:@Input()和@Output()

上面说了输入和输出在html上的表现形式,但是还需要两个注解才能完成html和ts文件的输入输出的对接。

@Input() 是负责输入的,配套和html上的[]使用,在ts里面标记productList属性,意思是productList的值由外部传入。

@Output()是负责输出,配套和html上的()使用,在ts里面标记onProductSelected属性,意思是onProductSelected这个值输出到外部。

关于EventEmitter<T>:

  这是事件触发器,在适当的时候通过EventEmitter触发事件,是一个帮你实现观察者模式的对象(管理一系列订阅者并向其发布事件的对象)

  例子:

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

// - > "Hello Nate"

  当我们把一个EventEmitter赋值给一个输出的时候,Angular会自动帮我们订阅事件。我们不需要自己订阅(当然,也可以自行实现自己的订阅逻辑)

ProListComponent还定义了两个函数clicked(点击)、isSelected(选择)

来看html怎么使用这两个函数的:

<div class="ui items">
  <app-product-row
    *ngFor="let myProduct of productList"
    [product]="myProduct"
    (click)="clicked(myProduct)"
    [class.selected]="isSelected(myProduct)"
  ></app-product-row>
</div>

  

click是内置事件,=号右边的clicked是ProListComponent定义的,实际参数是myProduct。

[class.selected]是用来设置元素的class属性,如果isSelected返回true,这个就会被赋值成true,就是选中。

*ngFor是循环指令,放在app-product-row标签上就是循环这个标签,语法是 *ngFor="let item of list" 或者 *ngFor="#item of list"

  list是需要循环的数组(这里的值来自ProListComponent定义的productList属性)

  item是迭代体,每次循环的当前对象。

所以上述html中

  [product]="myProduct" 将每个myProduct赋值给对应app-product-row属性product。

  (click)="clicked(myProduct)" 按照输出原则,是将app-product-row组件上的myProduct对象输出到ProListComponent的clicked方法内,由于是内置事件,我们就不用在app-product-row的ts文件额外弄click属性和对应的@Output()了。

 

接下来是app-product-row组件定义:

  

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

@Component({
  selector: 'app-product-row',
  templateUrl: './product-row.component.html',
  styleUrls: ['./product-row.component.css'],
  host: {'class': 'item'}
})
export class ProductRowComponent implements OnInit {
  @Input() product: Product;

  constructor() {
  }

  ngOnInit() {
  }

}

  这里只有host属性值得一说。

host:

  这里的用处是每个<app-product-row>标签都会自动添加class=‘item属性’,等价于<app-product-row class=‘item’></app-product-row>

  这样如果以后很多个地方用到这个组件就不用每个页面都在标签上这样写<app-product-row class=‘item’></app-product-row>

注意:如果开发工具对于这个host属性报红,没关系,是TSLint的问题,建议我们用@HostBindings and @HostListeners来代替host属性,无视掉。继续用host属性

  错误提示:TSLint: Use @HostBindings and @HostListeners instead of the host property (https://angular.io/styleguide#style-06-03) (use-host-property-decorator)

html:

<app-product-image [product]="product"></app-product-image>
<div class="content">
  <div class="header">{{product.name}}</div>
  <div class="meta">
    <div class="product-sku">SKU # {{product.sku}}</div>
  </div>
  <div class="department">
    <app-product-department [product]="product"></app-product-department>
  </div>
</div>
<app-price-display [price]="product.price"></app-price-display>

  

剩下的三个组件都差不多了,可以举一反三。

唯一要说的是app-product-image的html

<img class="product-image" [src]="product.imageUrl">

 

注意:这里不能用src="{{product.imageUrl}}",必须要[src]=“product.imageUrl”。

  因为浏览器在Angular运行起来之前就加载了这段模板,就会用{{product.imageUrl}}去加载图片,由于angular没有运行,浏览器识别不来这个语法,会得到一个404的错误。

  所以要用[src]的方式来写,这样是告诉angular这个img标签src属性的输入,才能取得到图片

 

最后将index.html页面修改为这样:

<!doctype html>
<html>
<head>
  <base href="">
  <title>ng-book 2: Inventory App</title>
  <link rel="icon" type="image/png" href="assets/images/favicon-32x32.png" sizes="32x32" />
  <link rel="icon" href="assets/images/favicon.ico" />

  <!-- Stylesheet -->
  <link rel="stylesheet" type="text/css" href="assets/vendor/styles.css">
  <link rel="stylesheet" type="text/css" href="assets/vendor/semantic.min.css">
  <!--<link rel="stylesheet" type="text/css" href="styles.css">-->
</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-inventory-app></app-inventory-app>
  </div>

</body>
</html>

  

样式文件来github上面下载整个项目源码吧!

附上项目github:https://github.com/617355557/ng2InventoryAppDemo

posted on 2018-12-28 15:32  康纳酱  阅读(293)  评论(1)    收藏  举报

导航