Создание директивы оболочки (оберните некоторый контент/компонент) в angular2

Я довольно новичок в разработке директив с Angular2. Я хочу создать директиву всплывающего окна, которая будет обертывать содержимое некоторыми классами CSS.

Контент

Содержимое может быть чистым текстом и заголовками, такими как:

<div class="data">
    <h2>Header</h2>
    Content to be placed here.
</div>

Затем я хочу присвоить этому директивный атрибут, например: popup.

<div class="data" popup>
    <h2>Header</h2>
    Content to be placed here.
</div>

Что должна делать директива, так это обернуть div внутрь, скажем:

<div class="some class">
    <div class="some other class">
        <div class="data">
            <h2>Header</h2>
            Content to be placed here.
        </div>
    </div>
</div>

Случай, который я описал до сих пор, это атрибут или структурные директивы.

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
  selector: `[popup]`
})

export class PopupDirective {


}

person Mikkel    schedule 15.12.2016    source источник


Ответы (2)


Другой ответ связан, но отличается.

Для более подробного ознакомления см. это: Как условно обернуть div вокруг ng-content — мое решение предназначено для Angular 4, но в связанном вопросе есть некоторые подсказки о том, как это может быть выполнимо для Angular 2.

Я решил эту проблему с помощью компонента и директивы. Мой компонент выглядит примерно так:

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

@Component({
  selector: 'my-wrapper-container',
  template: `
<div class="whatever">
  <ng-container *ngTemplateOutlet="template"></ng-container>
</div>
`
})
export class WrapperContainerComponent {
  @Input() template: TemplateRef<any>;
}

и моя директива, как это:

import { Directive, OnInit, Input, TemplateRef, ComponentRef, ComponentFactoryResolver, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[myWrapperDirective]'
})
export class WrapperDirective implements OnInit {

  private wrapperContainer: ComponentRef<WrapperContainerComponent>;

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainerRef: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver
  ) { }

  ngOnInit() {
    const containerFactory = this.componentFactoryResolver.resolveComponentFactory(WrapperContainerComponent);
    this.wrapperContainer = this.viewContainerRef.createComponent(containerFactory);
    this.wrapperContainer.instance.template = this.templateRef;
  }
}

Чтобы иметь возможность динамически загружать ваш компонент, вам нужно указать его как entryComponent внутри вашего модуля:

@NgModule({
  imports: [CommonModule],
  declarations: [WrapperContainerComponent, WrapperDirective],
  exports: [WrapperContainerComponent, WrapperDirective],
  entryComponents: [WrapperContainerComponent]
})
export class MyModule{}

поэтому HTML в конце:

<some_tag *myWrapperDirective />

Что отображается как:

<my-wrapper-container>
  <div class="whatever">
    <some_tag />
  </div>
</my-wrapper-container>
person Dan Field    schedule 18.08.2017
comment
Откуда берется управление в методе set @Input? Кроме того, откуда взялась SubmissionGroup? Не могу найти на него документацию. - person Finnur Eiríksson; 06.12.2017
comment
Ух ты. Это из-за плохой вставки копии из реального проекта, для которого я это сделал. Я почищу его, когда у меня будет несколько... - person Dan Field; 07.12.2017
comment
Это соответствующий ответ, не могу понять +7 на другом... - person n00dl3; 22.01.2018
comment
Спасибо за очистку этого @ n00dl3 - была поспешная работа по копированию и вставке, и я просто продолжаю терять это из виду, я не работаю в Angular 4 так много ATM - person Dan Field; 22.01.2018
comment
Что такое FormInputContainerComponent? Должно ли это быть WrapperContainerComponent в этом примере? - person Tyler Rick; 16.02.2018
comment
Я не мог заставить его работать (точно скопировал, за исключением изменения FormInputContainerComponent, как указано выше). Ошибок нет, но отображается как <!---->. Есть идеи, что я сделал не так? (Или есть рабочий Plunkr?) - person Tyler Rick; 16.02.2018
comment
Приближаемся... Помогает, когда вы используете ` *myWrapperDirective` вместо *myWraperDirective :) Удивлен, что он не выдал ошибку, что не распознал это имя. - person Tyler Rick; 16.02.2018
comment
Похоже, что когда вы оборачиваете ввод таким образом, ссылки на элементы # работают неправильно. Например, это всегда показывает null для ошибок: <input type="text" name="age" #ageRef="ngModel" [(ngModel)]="age" required *myWrapperDirective /> <pre>{{ageRef?.errors | json}}</pre> Это работает, если вы заключаете как <input>, так и код, использующий ссылку на элемент, в один и тот же <div *myWrapperDirective> ... но это не работает, если вам нужна ссылка на элемент во всем родительском элементе. составная часть. Любая идея, как экспортировать элемент ref для использования вне директивы? - person Tyler Rick; 16.02.2018
comment
Есть ли способ заставить его работать с проекцией (ng-content), а не * ngTemplateOutlet, пожалуйста? - person Youness Houdass; 05.05.2020

Вы можете добиться этого с помощью селектора атрибутов компонента и Angular 2 Content Projection <ng-content>

@Component({
  selector: 'my-app',
  template: `
    <div class="app"> 
        <div class="data" myWrapper>
            <h2>Header</h2>
            Content to be placed here.
        </div> 
    </div>
  `
})
export class AppComponent {}


@Component({
  selector: '[myWrapper]',
  template: `
    <div class="my-class">
      <div class="my-sub-class">
          <ng-content></ng-content>
      </div>
    </div>
  `
})
export class MyComponent {

}
person Kld    schedule 15.12.2016
comment
Очевидно, это относится только к элементам, которые принимают контент. Если на то пошло, он не будет работать при использовании на элементе ввода. - person user776686; 08.07.2017
comment
На самом деле это не похоже на то, о чем спрашивает @Mikkel. Это будет обертывать внутреннюю часть элемента, на котором размещен [myWrapper], а не оборачивать этот шаблон вокруг информации, на которой он размещен. - person Jessy; 24.07.2017
comment
Это прекрасно работает. Использовал это с angular 7. Но еще одна вещь: вместо директивы используйте его как компонент, поскольку [compname] не рекомендуется руководством по стилю angular. - person Swapnil Mhaske; 26.02.2019
comment
Это не то, о чем просят - это оборачивает содержимое элемента директивой, а НЕ АКТУАЛЬНЫЙ компонент директивой. В вашем примере <div class="data" myWrapper> должен быть обернут. - person Antoniossss; 10.06.2020