Какая точная грамматика для структурных директив Angulars - PullRequest
2 голосов
/ 09 мая 2019

Angulars Документация объясняет, что структурные директивы, такие как <p *ngIf="a as b"></p>, "десугарированы" в <p [ngIf]="a" [ngIfAs]="b">.

В десугаринге используется microsyntax, который позволяетвыражения типа

let node; when: hasChild
a as b
let x of y; index = i; trackBy: f

В документации приводятся некоторые примеры микросинтаксиса и предлагается изучить источники ngIf, но нет формального определения.

Что такое грамматикадля микросинтаксиса угловых структурных директив?

Ответы [ 2 ]

0 голосов
/ 09 мая 2019

Вы можете проверить мою интерактивную ДЕМО

enter image description here

Итак, в чем здесь логика?

Как только Angular встречает структурную директиву, он пытается ее проанализировать:

 *dir="..."
/\
indicates that it's a structural directive

В начале есть 3 случая:

*dir="expr
       \/
   [dir]="expr"

*dir="let var  // also var can have value
       \/ // starts with keyword 'let', indicates variable
   [dir] let-x="$impicit"


*dir="as var
      \/ // starts with keyword 'as'
  let-x="$impicit" // it won't fail but your directive won't be instantiated

После выражения вы можете использовать ключевое слово as для определения входной переменной шаблона для этого выражения или разделитель, такой как пробел, двоеточие, точка с запятой или запятая:

*dir="expr as var
 \/
[dir]="exp" let-var="dir"

*dir="expr[ ]
*dir="expr:
*dir="expr;
*dir="expr,

Обратите внимание, что dir здесь рассматривается как первый ключ привязки шаблона.

Теперь пришло время для другого ключа или переменной:

*dir="expr key2
*dir="expr:key2
*dir="expr;key2
*dir="expr,key2

И мы можем присвоить значение этому ключу через пробел или точку с запятой:

*dir="expr key2 exp2
*dir="expr:key2 exp2
*dir="expr;key2 exp2
*dir="expr,key2 exp2
or
*dir="expr key2:exp2

И таким образом мы можем производить другие ключи. Эти ключи пишутся с заглавной буквы и соединяются с первым ключом.

*dir="expr key2 exp2 key3 exp3 ...
\/
[dir]="expr " [dirKey2]="exp2 " [dirKey3]="exp3"


let node; when: hasChild; otherKey: otherValue
  \/       \/      \/
  var     key    value
                         \/
dir [dirWhen]="hasChild" [dirOtherKey]="otherValue" let-node="$implicit"


*dir="let x of y; index = i; trackBy: f"
           \/
dir [dirOf]="y" [dirIndex]="= i" [dirTrackBy]="f" let-x="$implicit"

*dir="let x  of   y;  let index = i; trackBy: f"
        \/   \/   \/      \/           \/    \/
        var  key  value     var          key   value
                   \/
dir [dirOf]="y" [dirTrackBy]="f" let-x="$implicit" let-index="i"

Как вы можете видеть, мы можем либо определить ключ-значение, либо установить входные переменные шаблона * от 1037 * до let или как keywords

Если вы считаете, что Angular docs не полностью это объясняет, тогда вы можете следовать исходному коду

  // Parses the AST for `<some-tag *tplKey=AST>`
  parseTemplateBindings(tplKey: string): TemplateBindingParseResult {
    let firstBinding = true;
    const bindings: TemplateBinding[] = [];
    const warnings: string[] = [];
    do {
      const start = this.inputIndex;
      let rawKey: string;
      let key: string;
      let isVar: boolean = false;
      if (firstBinding) {
        rawKey = key = tplKey;
        firstBinding = false;
      } else {
        isVar = this.peekKeywordLet();
        if (isVar) this.advance();
        rawKey = this.expectTemplateBindingKey();
        key = isVar ? rawKey : tplKey + rawKey[0].toUpperCase() + rawKey.substring(1);
        this.optionalCharacter(chars.$COLON);
      }

      let name: string = null !;
      let expression: ASTWithSource|null = null;
      if (isVar) {
        if (this.optionalOperator('=')) {
          name = this.expectTemplateBindingKey();
        } else {
          name = '\$implicit';
        }
      } else if (this.peekKeywordAs()) {
        this.advance();  // consume `as`
        name = rawKey;
        key = this.expectTemplateBindingKey();  // read local var name
        isVar = true;
      } else if (this.next !== EOF && !this.peekKeywordLet()) {
        const start = this.inputIndex;
        const ast = this.parsePipe();
        const source = this.input.substring(start - this.offset, this.inputIndex - this.offset);
        expression = new ASTWithSource(ast, source, this.location, this.errors);
      }

      bindings.push(new TemplateBinding(this.span(start), key, isVar, name, expression));
      if (this.peekKeywordAs() && !isVar) {
        const letStart = this.inputIndex;
        this.advance();                                   // consume `as`
        const letName = this.expectTemplateBindingKey();  // read local var name
        bindings.push(new TemplateBinding(this.span(letStart), letName, true, key, null !));
      }
      if (!this.optionalCharacter(chars.$SEMICOLON)) {
        this.optionalCharacter(chars.$COMMA);
      }
    } while (this.index < this.tokens.length);

    return new TemplateBindingParseResult(bindings, warnings, this.errors);
}

Приведенный выше код описывает алгоритм синтаксического анализа структурной директивы.

0 голосов
/ 09 мая 2019

Согласно документации следующий код:

<div *ngIf="hero" class="name">{{hero.name}}</div>

превращается в:

<ng-template [ngIf]="hero">
  <div class="name">{{hero.name}}</div>
</ng-template>

Angular создает этот тег ng-template вокруг вашего div и оценивает if, прежде чем решить, добавлять ли div или нет. Шаблон ng - это фантомный тег, который не существует в DOM (не мешает CSS) и выполняет некоторую логику.

...