Как использовать сервис шаблонов для уменьшения HTML? - PullRequest
0 голосов
/ 06 февраля 2019

У меня есть компонент, который обертывает элементы управления формы.Это добавляет повторно используемый код для каждого элемента управления, но для простоты, скажем, все, что он делает, это добавляет метку:

app.component:

<app-wrapper labelText="First Name">
  <input type="text" >
</app-wrapper>

wrapper.component:

<div style="border:thin solid; padding:10px">
    <label>{{labelText}}</label>
  <ng-content></ng-content>
</div>

export class WrapperComponent {
    @Input() labelText;
}

Я бы хотел вместо этого сделать обертку @Directive, чтобы объем HTML в форме еще больше уменьшился, но я не могу понять, как он будет работать.Мои мысли примерно такие:

app.component:

@Component({
  selector: 'my-app',
  template: `

  <input type="text" wrapper labelText="First Name">
  <input type="text" wrapper labelText="Last Name" >

  <ng-template #tpl>
    <div style="border:thin solid; padding:10px">
        <label>{{labelText}}</label>
      <ng-content></ng-content>
    </div>
  </ng-template>`
})
export class AppComponent {
  @ViewChild('tpl', {read:TemplateRef}) tpl
  constructor(private service: TemplateService) { }

  ngAfterViewInit() {
    this.service.template = this.tpl;
  }
}

wrapper.dir

@Directive({
  selector: '[wrapper]'
})
export class WrapperDirective {
    @Input('labelText') labelText;

  constructor(
    private vc:ViewContainerRef,
    private service:TemplateService) { }

  ngAfterViewInit() {
    of(null).pipe(
      delay(1)).subscribe(() => {
        let tpl = this.service.template;
        this.vc.createEmbeddedView(this.service.template)
      })
  }

}

К сожалению, я считаю, что вместо метки ввода вместо этого ставится меткаперед.Как я могу вставить его впереди?

Другая проблема: как мне получить доступ к #labelText в шаблоне?

1 Ответ

0 голосов
/ 06 февраля 2019

Я бы создал компонент с селектором атрибутов, который вы можете установить в div, который оборачивает ввод.Что-то вроде:

import { Component, Input } from '@angular/core';

@Component({
  selector: 'div[wrapper]',
  template: `
    <label>{{labelText}}</label>
    <ng-content></ng-content>
  `,
  styles: [`
  :host {
    border:thin solid; 
    padding:10px;
  }
  `]
})
export class WrapperComponent {
  @Input() labelText: string;
}

и используйте его так:

<div wrapper [labelText]="'test text'">
    <input type="text">
</div>

https://stackblitz.com/edit/attribute-selector-component?file=src%2Fapp%2Fwrapper.component.ts

Доступ к labelText в шаблоне довольно прост.Вам просто нужно передать контекст (2-й параметр) в createEmbeddedView ({labelText: this.labelText}).Вы также можете вставить шаблон в качестве первого потомка представления, передав индекс (3-й параметр) в createEmbeddedView как 0.

https://angular.io/api/core/ViewContainerRef#createEmbeddedView

Если вы действительно хотите использоватьдиректива, это должна быть структурная директива, если вы хотите изменить положение элемента в DOM (добавьте обертку вокруг него).Таким образом, вы на самом деле получаете шаблон элемента и шаблон для оболочки, и ваша директива оборачивает их (конечно, вы также можете использовать службу шаблонов, если хотите ...):

import { Directive, Input, ViewContainerRef, TemplateRef, EmbeddedViewRef } from '@angular/core';

@Directive({
  selector: '[wrapperLabelText]',
})
export class WrapperDirective {
  private _labelText: string;
  private embedded: EmbeddedViewRef<any>;

  @Input('wrapperLabelText')
  get labelText() {
    return this._labelText;
  }
  set labelText(value: string) {
    this._labelText = value;
    if (this.embedded) {
      this.embedded.context.labelText = value;
    }
  };

  @Input('wrapperLabelTextWrapper')
  wrapperTemplate: TemplateRef<any>;

  constructor(
    private templateRef: TemplateRef<any>,
    private vc: ViewContainerRef,
  ) {

  }

  async ngAfterViewInit() {
    await new Promise(resolve => setTimeout(resolve));
    const ref = this.vc.createEmbeddedView(this.wrapperTemplate, {
      wrapped: this.templateRef,
      labelText: this.labelText,
    });
  }
}

ииспользуйте это как:

<input type="text" *wrapperLabelText="'test';wrapper:wrapperTemplate">
<input type="text" *wrapperLabelText="'test 2';wrapper:wrapperTemplate">

<ng-template #wrapperTemplate let-wrapped="wrapped" let-labelText="labelText">
  <div style="border:thin solid; padding:10px">
    <label>{{labelText}}</label>
    <ng-container *ngTemplateOutlet="wrapped"></ng-container>
  </div>
</ng-template>

https://stackblitz.com/edit/wrapper-directive?file=src/app/app.component.html

...