Какова точная грамматика для структурных директив Angulars

В документации 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, но не дает формального определения.

Какова грамматика микросинтаксиса структурных директив angular?


person keppla    schedule 09.05.2019    source источник


Ответы (2)


В документах Angular есть специальный раздел для грамматики структурных директив.

Вы можете ознакомиться с моей интерактивной ДЕМО

введите здесь описание изображения

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

Как только 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"
               

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

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

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

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

Смотрите также

person yurzui    schedule 09.05.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 и оценивает его, прежде чем решить, добавлять ли div или нет. Шаблон ng — это фантомный тег, который не существует в DOM (не мешает css) и выполняет некоторую логику.

person Eduardo Vargas    schedule 09.05.2019
comment
Я понимаю, что теги шаблона будут добавлены. Что меня интересует, так это спецификация того, КАК происходит перевод микросинтаксиса в эти шаблоны. Рассмотрим let node; when: hasChild a as b и let x of y; index = i; trackBy: f: это гораздо больше. - person keppla; 09.05.2019