angular angular2-template

Angular2: ¿Cómo se expande ngfor?



angular2-template (2)

Sé que las reglas del libro de texto sobre eso <div *ngFor="let foo of foobars">{{foo.stuff}}</div> convierte en <template ngFor let-foo="$implicit" [ngForOf]="foobars"><div>...</div></template> . Mi pregunta es doble:

  • ¿CÓMO?
  • ¿Qué debo hacer para aprovechar este mecanismo ("microsintaxis")?

Es decir, convertir <div *myDirective="item">{{item.stuff}}</div> en <template myDirective let-item="$implicit"><div>{{item.stuff}}</div></template> ?

Desde que leí el código fuente de ngFor de arriba a abajo, solo puedo suponer que esta magia oscura está en el compilador en alguna parte, he estado arriba y abajo del github angular, pero no puedo señalarlo. ¡Ayuda!


Sí, toda la magia ocurre en el compilador.

Tomemos esta plantilla:

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

Primero se transformará a lo siguiente:

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

Y entonces:

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

En Angular2 rc.4 se ve así

Primero se genera un nodo de árbol ast (nodo de árbol de sintaxis abstracta) y luego toda la magia ocurre en TemplateParseVisitor.visitElement ( https://github.com/angular/angular/blob/2.0.0-rc.4/modules/%40angular/compiler/src/template_parser.ts#L284 ) específicamente en la parte inferior ( 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;

Este método devuelve EmbeddedTemplateAst . Es lo mismo que:

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

Si quieres girar:

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

dentro

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

entonces necesita usar la siguiente sintaxis:

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

Pero en este caso no pasarás contexto. Su directiva estructural personalizada podría verse así:

@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 } }

Y puedes usarlo como:

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

Espero que esto le ayude a comprender cómo funcionan las directivas estructurales.

Actualizar:

Veamos cómo funciona ( plunker )

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

Entonces puede escribir la siguiente directiva:

@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] }

Aquí está el Plunker correspondiente

Ver también

Ejemplo en vivo que puedes encontrar aquí https://alexzuza.github.io/enjoy-ng-parser/


*ngFor , *ngIf , ... son directivas estructurales.

Aplíquelo en un elemento <template> o prefije con un *

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