angular2实用技巧点滴

1. *ngFor

*ngFor指令定义了一些行属性:

export declare class NgForRow {
    $implicit: any;
    index: number;
    count: number;
    constructor($implicit: any, index: number, count: number);
    first: boolean;
    last: boolean;
    even: boolean;
    odd: boolean;
}

它们只能用在*ngFor语句中,如果在语句外部使用我们需要导出为局部变量。


    {{index + ". " + hero.name}}

$implicit为当前行本身,实际上就是我们这里的单个hero。其它几个属性都见名知意。

*ngFor的语句实际上属于Angular特有的微语法,它被Angular自身解析。

*ngFor正因为常常操作大量数据而应当在性能上被我们格外关注,首先*ngFor的改变规则

  1. 添加项目时,模板的新实例将添加到DOM。
  2. 当项目被删除时,其模板实例将从DOM中删除。
  3. 当项目重新排序时,它们各自的模板在DOM中重新排序。
  4. 否则,该项的DOM元素将保持不变。

以上的情况,*ngFor更新我们的视图都保持足够节源。

但也有些例外,比如当我们改变heroes的引用

heroes = heroes.concat(newHero);

*ngFor将重新渲染所有DOM,即便它只是增加了一个英雄。这种情况尤其发生在我们的列表数据从服务器更新下来时,它的每一次更新都差不多是一次引用的转变。

Angular为我们提供了NgForTrackBy指令,它被设置为组件内部类的一个函数,该函数要求返回资源被判断为没有变化的依据。

*ngFor = "let hero of heroes trackBy:trackByHeroes"

trackByHeroes(index : number, hero : Hero){
    return hero.id;
}

它可以接收两个参数,当前项索引和当前项原值,这里我们返回英雄的id值,表明两个id值如果一致,则他们是同一个英雄,此时不需要完全重新渲染该dom,而是复用它。

2. 指令加*的原因

语法糖,将总是展开为模板,例如

example

*语法糖直接展开为


其中,ngIf使用方括号使得show作为变量解析,而不是通常html语义上的直接属性。

什么情况下使用*语法糖?

所有直接性控制元素渲染与否的指令需要使用*,例如*ngFor*ngIf*ngSwitchCase*ngPluralCase,它们都将被包裹在template内,根据表达式值判定是否被渲染。而ngSwitchngPlural总是显示,它们的下级指令才直接控制渲染,所以不使用*语法糖。

其它情况的指令,一般遵守一个规则:

  1. 需要从元素流入数据到内部类,使用()包裹指令,例如所有事件都是从元素触发从而流入内部类引发控制,所以事件指令都是用()
  2. 需要从内部类流出数据到元素,使用[]包括指令,例如ngIfngForngSwitch等,它们都需要以内部类的变量为判断依据,所以都需要使用[],只是ngIfngFor等由于涉及控制元素是否渲染,它们需要template参与,所以提供了*语法糖简化编写,内部依然是[]
  3. 需要双向流动数据的,使用[()],例如[(ngModel)]就是一个典例。

2. 模板输入变量和模板引用变量

在模板上定义,作用域只在模板内部的普通变量称为模板输入变量。例如

  • 其中

    let hero
    

    定义了一个模板输入变量,它不能在模板外部使用。

    模板引用变量是模板中对 DOM 元素或指令的引用,可在同一元素、兄弟元素或任何子元素中被使用。例如

    //原生dom对象,使用#定义
    
    
    //原生dom对象,使用规范的ref-定义
    
    
    //被Angular封装的form对象
    
    //提交按钮被Angular内置的表单验证控制

    3. 安全导航操作符?.和管道操作符|

    this is my name {{my.name}}
    

    类似上面的表达式,my对象如果为null将导致应用报错。我们可以手动判断,例如

    this is my name {{my?my.name:''}}
    
    this is my name {{my && my.name}}
    

    应对这种情况,Angular提供了比较优雅的一个表达式操作符?.,当遇到空值时跳出,避免应用出错。更重要的是,它非常适合多重路径的处理。

    this is my name {{my?.name}}
    this is the test {{a?.b?.c?.d}}
    

    至于管道操作符,它可以一级一级的流动,还可以使用:添加管道条件。

    4. Angular的插值表达式什么不被支持?

    • 赋值 (=, +=, -=, ...)
    • new运算符
    • 使用;或,的链式表达式
    • 自增或自减操作符 (++和--)
    • 不支持位运算|和&
    • 具有新的模板表达式运算符,比如|和?.

    除以上官方明确说明的不被支持,实际上也有更多是不支持的,例如console、typeof/instanceof操作符等。但我们能做到吗?当然可以,在内部类声明方法即可,模板可以调用内部类方法,所以也就解开了插值表达式的限制。例如我们需要判断是否数组:

    //*.component.ts
    isArray(arr){
      return Array.isArray(arr);
    }
    
    //*.component.html
    *ngIf="isArray(arr)"
    

    5. 分清property属性绑定和Attribute绑定

    //完全等同的两种绑定
    
    
    

    Angular只允许绑定元素已有的原生属性,例如img的src属性,不存在的会报错。

    
    

    这时候我们只能使用Attribute绑定:

    
    

    像这种绑定方式还有下面的场景:

    
    
    
    //当然,我们更倾向于使用ngClass来批量管理类名
    
    
    cmx
    cmx
    
    //跟单位
    cmx
    
    //驼峰命名
    cmx
    
    //当然,我们还是有ngStyle作为批量管理的选择
    cmx
    

    5. 路由返回两种方式

    1. 路由
    constructor(
        private router : Router,
        private route : ActivatedRoute
    ){}
    this.router.navigate(['../'],{relativeTo:this.route});
    
    1. location
    constructor(
        private location : Location
    ){}
    this.location.back();
    

    6. 信任安全的值

    angular2有自身的一套安全过滤系统,例如,动态绑定一个url,angular2会自动把它无害化,诸如使用:unsafe:xxx的手段。但有时候它会导致我们得不到预期的运行结果,例如当我们使用URL.createObjectUrl用于预览本地选择的图片时,直接将其对象赋值给img标签的src通常会由于该安全机制而失败。如果我们确信自己是对的,就有必要使用angular2提供的api信任它。

    //注入DomSanitizer
    constructor(
        private sanitizer : DomSanitizer
    ) { }
    
    //根据需要调用下面的方法之一
    sanitizer.bypassSecurityTrustHtml(html)
    sanitizer.bypassSecurityTrustScript(script)
    sanitizer.bypassSecurityTrustStyle(style)
    sanitizer.bypassSecurityTrustUrl(url)
    sanitizer.bypassSecurityTrustResourceUrl(rurl)
    

    7. 编写一个图片加载完成的指令

    目前的情况看来,angular2并没有提供图片load的事件绑定,有需要的话,自己编写也并不难

    @Directive({
      selector: 'img[loaded]'
    })
    export class ImgLoadedDirective {
    
      @Input()
      loaded : any
    
      @Input()
      data : any
    
      constructor(renderer : Renderer, el : ElementRef) {
        renderer.listen(el.nativeElement, 'load', () => {
          this.loaded(this.data);
        });
      }
    
    }
    
    

    指令监听img标签的loaded属性,传入一个方法名,有必要的话还可以传入data,当img触发load事件时,会自动调用该方法并传入data。

    //html
    ![](...)
    
    //ts
    load(data){
        dosomething;
    }
    

    8. ngFor中同时使用ngIf

    在一个*ngFor中,如果同时使用了*ngIf将导致转化为template出错,那么*ngFor中我们如果也想条件性渲染部分跳过呢?放弃语法糖可以做到

    {{hero.name}}

    /*====================*/ //只显示cmx除外的英雄

    9. 使用hash风格的路由

    angular2默认采用HTML5的pushState来管理路由,它会导致前端路由与后端路由的冲突,例如当部署到nginx环境时,我们通过首页进入子路由一切正常,但是在子路由路径下,刷新就会报404了。默认情况下nginx会当成这个路径是实际web路径下的资源而去定位它,但可想而知实际是并不存在的。折中的方案可以改回hash风格:

    RouterModule.forRoot(routes, {useHash: true})
    

    你可能感兴趣的:(angular2实用技巧点滴)