Angular2入门系列教程3-多个组件,主从关系

来源:http://www.prospettivedarte.com 作者:计算机教程 人气:171 发布时间:2019-05-10
摘要:用ngModel绑定radio单选框 当从页面上input的value="true"/"false"获取的值是字符串,此时不能直接做布尔计算,需要函数进行下转化。 html: 是否直接写入ERPlabel input type="radio" name="directly" [(ng

用ngModel绑定radio单选框

当从页面上input的value="true"/"false"获取的值是字符串,此时不能直接做布尔计算,需要函数进行下转化。

html:

是否直接写入ERP
<label>
    <input type="radio" name="directly" [(ngModel)]="saveData.istoerp" #directly="ngModel" value="true" (onClick)="directlyChange($event)">
                是
 </label>
 <label>
      <input type="radio" name="directly" [(ngModel)]="saveData.istoerp" #directly="ngModel" value="false" (onClick)="directlyChange($event)">
                否
 </label>

ts:

directlyChange(e) {//是否写入ERP变化
    if(String(e)=="true"){
        this.saveData.istoerp=Boolean(1);
    }else{
        this.saveData.istoerp=Boolean(0);
    }
}

  

某些有用的生命周期

  • ngDoCheck:可监测每次input传参对象改变,例子如上
  • ngAfterViewChecked:每次完成界面渲染

    ### 使页面强制刷新

    如同AngularJS中的$apply()

使用如下:

import { ChangeDetectorRef} from '@angular/core';

@Component({
       selector: "...",
       templateUrl: '...',
       styleUrls: ['...']
})
export class ..Component implements OnInit {

    constructor(private changeDetectorRef:ChangeDetectorRef) {
    }

    Fun(){
        this.changeDetectorRef.detectChanges();//需要强制刷新
    }
}

  项目已经放到了gitbub上,地址 https://github.com/SeeSharply/LearnAngular

当ngModel双向绑定非基本数据类型值时

子组件中ngModel绑定的值改变时,通过onChangeCallback 传回父组件时,有两种情况:

  1. 基本数据类型:string/number 等变量,父组件中会跟着变化
  2. 非基本数据类型:{}/[]/Date/... 父组件中不会检测到变化

解决方法:

创建一个新对象传回:

this.onChangeCallback(new Date(this.date));

(顺便完善父子组件间双向数据绑定的实现)

子组件ts:

import { Component,forwardRef} from '@angular/core';
import { ControlValueAccessor,NG_VALUE_ACCESSOR, DefaultValueAccessor } from '@angular/forms';

@Component({
  selector: 'iq-timepicker',
  templateUrl: 'timepicker.component.html',
  providers:[{
    provide:NG_VALUE_ACCESSOR,
    useExisting:forwardRef(()=>TimePickerComponent),
    multi:true
  }]
})
export class TimePickerComponent implements ControlValueAccessor{
  private date:Date;
  private onChangeCallback: any = {};
  private onTouchedCallback: any = {};

  private triggerChange(){
    //此时子组件中 this.date 已变化
    let d = new Date(this.date);
    this.onChangeCallback(d);//变化传给父组件
  }
  ...

  writeValue(value: Date) {
    if(!value){
      this.date=new Date();
    }else{
      this.date = new Date(value);
    }
  }
  registerOnChange(fn) {
    this.onChangeCallback = fn;
  }
  registerOnTouched(fn) {
    this.onTouchedCallback = fn;
  }

父组件html中使用:

<iq-timepicker [(ngModel)]="timeVariable"></iq-timepicker>

  由于article是html自带的标签,我们使用ngarticle作为选择器

当select下拉选项变化时

有种情况,在同一个页面,其他操作导致select的下拉选项发生变化,不刷新页面。

此时需要重置已选的项:

  • 已选的项没有删除,保持选中状态
  • 已选的项删除,则置为”请选择”

实现难点:

  • 当获取新的下拉列表时,需强制刷新界面;
  • 当判断已选的项目回来时,不仅需要设置双向绑定的值val,还要重置 select对象的已选index和显示text

html:

<tr *ngFor="let item of procurementList;index as i">
    <td>
        <select [(ngModel)]="item.MaterialSource" id="materialSource{{i}}" name="materialSource{{i}}" ngModel required>
            <option value=''>请选择</option>
            <option *ngFor="let em of contractList" [ngValue]="em.id">{{em.value}}</option>
        </select>
    </td>
</tr>

ts:

import { Component, OnInit, DoCheck,ChangeDetectorRef} from '@angular/core';
declare var $:any;

@Component({
       selector: "...",
       templateUrl: '...',
       styleUrls: ['...']
})
export class ..Component implements OnInit {

procurementList;
contractList;//合同列表(下拉列表数据)结构为{"id":"","value":""}...

constructor(private changeDetectorRef:ChangeDetectorRef) {
}

ngDoCheck(){
    if (JSON.stringify(this.contractList) != window.localStorage.getItem("contractList")) {//合同列表变化
        this.contractList = JSON.parse(window.localStorage.getItem("contractList"));
        this.changeDetectorRef.detectChanges();//需要强制刷新
        for(let i=0,len=this.procurementList.length;i<len;i  ){
            //重新检查设置已选
            let pro=this.procurementList[i];
            let list=this.OnlyIdContract(pro["MaterialSource"]);
            if(!list){
                pro["MaterialSource"]="";//为请选择
            }else{
                pro["MaterialSource"]=list["em"]["id"];//val
                $("#materialSource" i)[0].selectedIndex = list["index"] 1; //index
                $("#materialSource" i)[0].text=list["em"]["value"]; //text
            }
        }
    }
}
OnlyIdContract(id){//根据合同唯一(id)标识 匹配合同项
    let list={
        em:"",//匹配下拉的项
        index:""//匹配已选在现有列表中的index
    }
    let item;
    for(let i=0,len=this.contractList.length;i<len;i  ){
        item=this.contractList[i];
        if(item.id==id){
            list.em=item;
            list.index=i;
            return list;
        }
    }
    return "";//已选的已经不在合同列表中 则置空
    }
}
.articleList {
    list-style: none;
}

.articleList li {
    position: relative;
    width: 100%;
    font-size:16px;
    border-bottom: 1px dashed #d9d9d9;
    box-sizing: border-box;
    border-radius:2px;
}
.articleList li > a {
    position:relative;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 15px 25px;
    display: block;
    width: 100%;
    color:#333;
    height:100%;
}
.articleList li > span {
    position:relative;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 15px 25px;
    display: block;
    color:#000;
    height:100%;
    background:69aa6f
}
.articleList li > a:hover {
    color: #69aa6f;
    transition: color 0.3s, box-shadow 0.3s;
} 
.articleList li:hover {
    background: #efefef;
}

父组件向子组件传值

方式:

  1. 属性输入 @Input()
  2. 双向绑定 writevalue

注意:

  • 传入顺序,先Input后writevalue
  • 两者当传值为非基本数据类型时,都无法监测改变

    Input方式:@Input() set 方法 或者 ngOnChanges,都无法监测改变。像这样:

      //子组件
      import { Component, OnInit, Input, OnChanges,SimpleChanges } from '@angular/core';
    
      @Component({
          selector: "NB-new-list",
          templateUrl: '...',
          styleUrls: ['...']
      })
      export class NBNewListComponent implements OnInit,OnChanges {
    
          @Input() currency;//币种(此时是基本数据类型)
          ngOnChanges(changes: SimpleChanges){
              if(changes["currency"]){//币种 变化
                  //变化前的值:changes["currency"].previousValue
                  //变化后的值:changes["currency"].currentValue
                  ...
              }
          }
      }
    

    解决方法:

    可在生命周期ngDoCheck中监测变化:

      //子组件
      import { Component, OnInit, Input, DoCheck } from '@angular/core';
    
      @Component({
          selector: "NB-new-list",
          templateUrl: '...',
          styleUrls: ['...']
      })
      export class NBNewListComponent implements OnInit {
    
          beforeList;//暂存上一个清单数据
          @Input() purchaseData=[];//清单数组(此时是非基本数据类型)
          ngDoCheck(){
              if (JSON.stringify(this.purchaseData) != JSON.stringify(this.beforeList)) {//当清单变化
                  this.beforeList = JSON.parse(JSON.stringify(this.purchaseData));
                  ...
              }
          }
      }
    

    注意:

在ngDoCheck中进行input传入值的前后比较时,如果是比较长的对象或数组,建议把上一个值保存为字符串,否则有些变化无法检测到

形如:

ngDoCheck() {
    if (JSON.stringify(this.purchaseData) != this.beforeListStr){//采购清单 变化
        this.beforeListStr = JSON.stringify(this.purchaseData);
    }
}

  打开我们的App.Module.ts,我们像声明AppComponent一样声明我们的ArticleComponent,方法就是在declaration中添加我们的组件,这个declaration数组就是我们的组件声明的地方,所有的组件都需要在这里声明之后才可以使用

  然后我们需要一个显示明细的东西,我们来做个显示明细编辑吧

  我们注意到这里又多了一个[(ngModel)],简单就理解为input一类标签的值绑定,双向的哦

.articledetail {
    margin:20px;
    width: 100%;
    margin: 0 auto;
}
h2{
    text-align: center;
}
.row{
    width: 100%;
    padding: 10px 20px;
}
.row>span{
    margin-left: 25px;
}
.myInput{
    height: 36px;
    line-height: 36px;
    border: 0px;
    border-bottom: 2px solid #000;
    padding: 6px 12px;
    box-shadow: 0px 5px 10px #ccc;
    width: auto;
}
import { Component, OnInit,Input } from '@angular/core';
import {BLOGS,Blog} from '../data/blog';

@Component({
    selector: 'article-detail',
    templateUrl: './articledetail.component.html',
    styleUrls:['./articledetail.component.css']
})

export class ArticledetailComponent implements OnInit {
    @Input() blog:Blog;
    ngOnInit() { }
}

  

  新建一个article-detail组件,和上边一样,文件夹

<div class="article">
    <ul class="articleList">
            <li *ngFor="let blog of blogList">
                <a>
                   {{blog.id}}:{{blog.title}}
                </a>
            </li>
    </ul>
</div>

  

  本文章的提交 https://github.com/SeeSharply/LearnAngular/tree/a877676846b22b6dbe5430d02b01d25fb5463c61

 


  好了,前面简单介绍了Angular2的基本开发,并且写了一个非常简单的组件,这篇文章我们将要学会编写多个组件并且有主从关系

  

图片 1

  article.component.ts

  完成之后,在我们的app.component.html中使用它

  

 

 

 错误显示我们的ngarticle是一个未知的组件,未知怎么办,注册嘛,所有的Angular组件都要注册(声明)之后才能使用,具体方法如下

  

  图片 2

  

  上一篇 Angular2项目初体验-编写自己的第一个组件

  我们看到article.component.ts中新增了一个属性selectedBlog,多了一个事件 selectBlog;这个事件通过(click)="selectBlog(blog)"绑定到了li标签的点击事件上,没错,Angular的点击事件就是这样么绑定的,然后我们在下方看到了我们的article-detail 组件的标签,里面有个 [blog]="selectedBlog",结合到刚才detail组件的@Input() blog:Blog 属性,我们就明白了,Input故名思议就是输入,可以接收父组件的参数;

  

  

<div class="article">
    <ul class="articleList">
            <li *ngFor="let blog of blogList" (click)="selectBlog(blog)">
                <a>
                   {{blog.id}}:{{blog.title}}
                </a>
            </li>
    </ul>
    <div>
    <article-detail [blog]="selectedBlog"></article-detail>

    </div>
</div>

  articledetail.component.html

export class Blog{
    id:number;
    title:string;
}
export let BLOGS:Blog[]=[
    { id:1,title:"号外号外?奥巴马要下台啦"},
    { id:2,title:"什么?奥巴马要下台啦?"},
    { id:3,title:"号外号外?川普要上台了"},
    { id:4,title:"啥?我大四川人也要当美国总统了?"},
    { id:5,title:"mdzz,一群麻瓜,统统查杀"},
    { id:6,title:"首推龙文,必须出具"}
]

本文由皇牌天下投注网发布于计算机教程,转载请注明出处:Angular2入门系列教程3-多个组件,主从关系

关键词:

上一篇:没有了

下一篇:没有了

最火资讯