簡體   English   中英

Angular2:ngfor 是如何擴展的

[英]Angular2: How is ngfor expanded

我知道關於<div *ngFor="let foo of foobars">{{foo.stuff}}</div>變成<template ngFor let-foo="$implicit" [ngForOf]="foobars"><div>...</div></template> 我的問題有兩個:

  • 如何?
  • 我自己需要做什么來利用這種機制(“微語法”)?

即把<div *myDirective="item">{{item.stuff}}</div>變成<template myDirective let-item="$implicit"><div>{{item.stuff}}</div></template> ?

由於我從上到下閱讀了 ngFor 的源代碼,我只能假設這個黑暗魔法在某個地方的編譯器中,我一直在 angular github 上下,但我不能把我的手指放在它上面。 幫助!

是的,所有的魔法都發生在編譯器中。

讓我們以這個模板為例:

<div *ngFor="let foo of foobars">{{foo}}</div>

首先它將被轉換為以下內容:

<div template="ngFor let foo of foobars>{{foo}}</div>

進而:

<template ngFor let-foo [ngForOf]="foobars"><div>{{foo}}</div></template>

在 Angular2 rc.4 中它看起來像這樣在此處輸入圖片說明

首先生成 ast 樹節點(抽象語法樹節點),然后所有魔法都發生在TemplateParseVisitor.visitElement ( https://github.com/angular/angular/blob/2.0.0-rc.4/modules/%40angular/ compiler/src/template_parser.ts#L284 )特別是在底部( https://github.com/angular/angular/blob/2.0.0-rc.4/modules/%40angular/compiler/src/template_parser.ts# L394 )

if (hasInlineTemplates) {
  var templateCssSelector = createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
  var templateDirectiveMetas = this._parseDirectives(this.selectorMatcher, templateCssSelector);
  var templateDirectiveAsts = this._createDirectiveAsts(
      true, element.name, templateDirectiveMetas, templateElementOrDirectiveProps, [],
      element.sourceSpan, []);
  var templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
      element.name, templateElementOrDirectiveProps, templateDirectiveAsts);
  this._assertNoComponentsNorElementBindingsOnTemplate(
      templateDirectiveAsts, templateElementProps, element.sourceSpan);
  var templateProviderContext = new ProviderElementContext(
      this.providerViewContext, parent.providerContext, parent.isTemplateElement,
      templateDirectiveAsts, [], [], element.sourceSpan);
  templateProviderContext.afterElement();

  parsedElement = new EmbeddedTemplateAst(
      [], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts,
      templateProviderContext.transformProviders,
      templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex,
      element.sourceSpan);
}
return parsedElement;

此方法返回EmbeddedTemplateAst 它與以下內容相同:

<template ngFor let-foo [ngForOf]="foobars"><div>{{foo}}</div></template>

如果你想轉:

<div *myDirective="item">{{item.stuff}}</div>

進入

<template myDirective let-item><div>{{item.stuff}}</div></template>

那么你需要使用以下語法:

<div *myDirective="let item">{{item.stuff}}</div>

但在這種情況下,您不會傳遞上下文。 您的自定義結構指令可能如下所示:

@Directive({
  selector: '[myDirective]'
})
export class MyDirective {
  constructor(
    private _viewContainer: ViewContainerRef, 
    private _templateRef: TemplateRef<any>) {}

   @Input() set myDirective(prop: Object) {
    this._viewContainer.clear();
    this._viewContainer.createEmbeddedView(this._templateRef, prop); <== pass context
  }
} 

你可以像這樣使用它:

<div *myDirective="item">{{item.stuff}}</div>

               ||
               \/

<div template="myDirective:item">{{item.stuff}}</div>

               ||
               \/

<template [myDirective]="item">
   <div>{{item.stuff}}</div>
</template>

我希望這能幫助你理解結構指令是如何工作的。

更新:

讓我們看看它是如何工作的( plunker

*dir="let foo v foobars" => [dirV]="foobars"

在此處輸入圖片說明

因此,您可以編寫以下指令:

@Directive({
  selector: '[dir]'
})
export class MyDirective {
  @Input()
  dirV: any;

  @Input()
  dirK: any;

  ngAfterViewInit() {
    console.log(this.dirV, this.dirK);
  }
}

@Component({
  selector: 'my-app',
  template: `<h1>Angular 2 Systemjs start</h1>
  <div *dir="let foo v foobars k arr">{ foo }</div>
  `,
  directives: [MyDirective]
})
export class AppComponent {
  foobars = [1, 2, 3];
  arr = [3,4,5]
}

這是相應的Plunker

也可以看看

您可以在這里找到現場示例https://alexzuza.github.io/enjoy-ng-parser/

*ngFor , *ngIf , ... 是結構指令。

要么將其應用於<template>元素,要么使用*前綴

https://angular.io/docs/ts/latest/guide/structural-directives.html#!#unless

 import { Directive, Input } from '@angular/core'; import { TemplateRef, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[myUnless]' }) export class UnlessDirective { constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef ) { } @Input() set myUnless(condition: boolean) { if (!condition) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } } }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM