Angular2:ngfor如何扩展

7
我知道在Angular中,<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上寻找过,但是我无法找到它。 帮帮我!

2个回答

14

是的,所有的魔法都发生在编译器中。

让我们来看一下这个模板:

<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中,它看起来像这样:enter image description here

首先生成抽象语法树节点(Abstract Syntax Tree node),然后所有魔法都发生在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

另请参阅

您可以在此处找到实时示例


是的!昨天我弄清楚了传递上下文的问题,并在编译器中找到了TEMPLATE_PREFIX,所以我已经有点接近了,但你的确认让我更加确定。 - TDaver
我不确定,但似乎 *dir="let foo of foobars" 将会是 [dirOf]="toolbars" - yurzui
1
*dir="let foo v foobars" => [dirV]="foobars"https://plnkr.co/edit/myE61PkX7UU4nAmGDQ1x?p=preview - yurzui
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/120087/discussion-between-yurzui-and-tdaver。 - yurzui
1
@yurzui,另外,你是怎么调试ts文件的?我已经创建了一个单独的问题。在最新的npm版本中,他们似乎将所有内容都打包在一起而没有 sourcemaps。 - Max Koretskyi
显示剩余4条评论

0

*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();
    }
  }
}

是的,我知道 :) 我是在询问更高级的做事方法。 - TDaver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接