Применить директиву условно


112

Я использую Материал 2 для добавления md-raised-button. Я хочу применить эту директиву, только если определенное условие будет выполнено.

Например:

<button md-raised-button="true"></button>

Другой пример: я создал базовую динамическую реактивную форму в plunker. Я использую formArrayNameдирективу реактивной формы для массива элементов управления. Я хочу применить formArrayNameдирективу, только если определенное условие станет истинным, в противном случае не добавляйте formArrayNameдирективу.

Вот ссылка на плункер .


Да, мд-поднятая кнопка, это директива атрибута ( material.angular.io/components/component/button )
user2899728

Применение условий, при которых используются директивы, скорее всего, сделает AoT бесполезным, поскольку вы не сможете скомпилировать шаблоны, если приложение не запущено.
Reactgular

Ответы:


55

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

<button *ngIf="!condition"></button>
<button *ngIf="condition" md-raised-button></button> 

Изменить: возможно, это будет полезно.


13
Благодарю за ваш ответ. Но я уже использовал эту технику в примере плункера, который я добавил подробно. Это хороший вариант, но не очень хорошая идея, если код сложный. Потому что эта техника разрушает концепцию повторного использования. Вы можете увидеть мой пример в plunker и заметить, как мой код дублируется из-за этого подхода. Вот почему я не хочу использовать эту технику. Мне нужно лучшее решение, чтобы я мог создавать динамические элементы с помощью.
user2899728

1
Понимаю. Вы можете обернуть код, который дублируется в компоненте.
LLL

3
Это хорошая идея, но, к сожалению, она не работает в случае реактивных форм. С реактивными формами возникает другая проблема. Если я помещаю ввод в отдельный компонент, то angular потребует от меня добавить [formGroup] = "form" в этот дочерний компонент. Но если я это сделаю, привязка моего массива не будет работать.
user2899728

15
Это ужасное решение, поскольку оно дублирует код. Представьте, что это не просто кнопка, а div с большим количеством html-кода внутри.
Шахар Хар-Шув

Нет необходимости включать formGroupв дочерний компонент, который является оболочкой, inputпоскольку вы можете просто передать свой formControlкак @Input() control: FormControlвнутренний компонент оболочки и применить его к родному inputчерез[formControl]="control"
Олег Куйбар,

88

Если вам просто нужно добавить атрибут для запуска правил CSS, вы можете использовать следующий метод: (это не создает / уничтожает директиву динамически)

<button [attr.md-raised-button]="condition ? '' : null"></button>

То же самое применимо и к плунжеру: вилка

Обновить:

Как condition ? '' : nullработает значение:

Когда это пустая строка ( ''), она становится attr.md-raised-button="", когда ее nullатрибут не существует.

Обновление: обновление plunker: fork (проблемы с версией исправлены, обратите внимание, что вопрос изначально был основан на angular 4)


@Luke Да, если вы имеете в виду под "(сейчас)" как: с 2017-01-06, то есть за 5 месяцев до того, как был задан вопрос. см.
журнал

@Aldracor angular 5.2.1, не работает. И я не понимаю, почему это должно выглядеть как «условие?»: Null », где оба результата в основном ложны. Что должно быть вместо ""? «правда» тоже не работает.
Арсений Фомин

1
поэтому для тех, кто не хочет создавать элемент html для работы своей директивы и использует ng-container, я просто
передал

7
У меня не работает в Angular 6. Образец плункера не выходит за пределы экрана загрузки. Насколько я знаю, этот подход может работать для атрибутов HTML, но не будет работать для директив Angular. Первоначальный вопрос касался директив Angular из библиотеки материалов ng.
JeffryHouser

6
@kbpontius, спасибо за комментарий, он работает с атрибутами Html, но не с директивами angular (как атрибут)
Реза

19

Как уже отмечалось, это невозможно. Одна вещь, которую можно использовать, по крайней мере, для предотвращения дублирования ng-template. Это позволяет извлекать содержимое элемента, затронутого ngIfветвлением.

Если вы, например, хотите создать компонент иерархического меню с использованием Angular Material:

<!-- Button contents -->
<ng-template #contentTemplate>
    <mat-icon *ngIf="item.icon != null">{{ item.icon }}</mat-icon>
    {{ item.label }}
</ng-template>

<!-- Leaf button -->
<button *ngIf="item.children == null" mat-menu-item
    (click)="executeCommand()"
    [disabled]="enabled == false">
    <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
</button>
<!-- Node button -->
<ng-container *ngIf="item.children != null">
    <button mat-menu-item
        [matMenuTriggerFor]="subMenu">
        <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
    </button>

    <mat-menu #subMenu="matMenu">
        <menu-item *ngFor="let child of item.children" [item]="child"></menu-item>
    </mat-menu>
</ng-container>

Здесь условно применяется директива matMenuTriggerFor, которая должна применяться только к пунктам меню с дочерними элементами. Содержимое кнопки вставляется в оба места через ngTemplateOutlet.


6
Это единственный ответ, который позволяет разработчику использовать условные директивы, а также возможность повторного использования кода.
Ravinder Payal

16

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

В классе директивы создайте входную переменную:

@Input('myDirective') options: any;

При применении директивы установите свойство apply входной переменной:

<div [myDirective] = {apply: someCondition}></div>

В методе директивы проверьте переменную this.options.apply и примените логику директивы на основе условия:

ngAfterViewInit(): void {
    if (!this.options.apply) {
        return;
    }

    // directive logic
}

3
У меня это не работает, поскольку директива все еще существует в компоненте, она просто не выполняет никакой логики. Я хотел бы, чтобы существование директивы применялось условно, особенно потому, что есть css, который проверяет эту директиву.
Ran Lottem

У вас другая проблема, отличная от указанной здесь (примените директиву условно). Опубликуйте свою проблему, и люди помогут вам. В качестве первой идеи вы можете поместить директиву в div и поместить вокруг него ng-контейнер с * ngIf, чтобы применить ваше условие. ngIf удаляет узел div из DOM, чтобы он мог работать. Я просто догадываюсь, вам нужно опубликовать свою проблему с примерами кода / описанием, чтобы ppl мог вам помочь.
Tiha

Это не лучшее решение. Директива все еще инициализирована.
Дино

8

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

Однако, если вы просто хотите переключить md-buttonстиль с плоского на приподнятый , тогда этот

<button md-button [class.mat-raised-button]="isRaised">Toggle Raised Button</button>

сделает свое дело. Plunker


3

Это тоже может быть решением:

[md-raised-button]="condition ? 'true' : ''"


Он работает для angular 4, ionic 3 следующим образом:

[color]="condition ? 'primary' : ''"где condition- функция, которая решает, активна это страница или нет. Весь код выглядит так:

<button *ngFor="let page of ..." [color]="isActivePage(page) ? 'primary' : ''">{{ page.title }}</button>


2

В настоящее время существует NOспособ условно применить директиву к компоненту. Это не поддерживается. Компоненты, которые вы создали, могут быть добавлены или удалены условно.

Уже существует проблема, созданная для того же самого angular2, так что это также должно быть в случае с angular4.

В качестве альтернативы вы можете выбрать вариант с ng-if

<button ngIf="!condition"></button>
<button ngIf="condition" md-raised-button></button> 


2

Мне не удалось найти хорошее существующее решение, поэтому я создал свою собственную директиву, которая делает это.

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

@Directive({
  selector: '[dynamic-attr]'
})
export class DynamicAttrDirective {
  @Input('dynamic-attr') attr: string;
  private _el: ElementRef;

  constructor(el: ElementRef) {
    this._el = el;
  }

  ngOnInit() {
    if (this.attr === '') return null;
    const node = document.createAttribute(this.attr);
    this._el.nativeElement.setAttributeNode(node);
  }
}

Тогда ваш html:

<div dynamic-attr="{{hasMargin: 'margin-left' ? ''}}"></div>


Вы можете использовать только @HostBinding('attr.dynamic-attr') @Input('dynamic-attr') attr: string;вместо ngOnInit + ElementRef.
pinguinjkeke

2
Это не ответ на вопрос, как динамически добавить директиву , а не атрибут.
Крис Хейнс

2

Может кому поможет.

В приведенном ниже примере у меня есть директива, my-button.component.htmlи я хочу применить *appHasPermissionее <button>только в том случае, если roleатрибут установлен.

<ng-container *ngIf="role; else buttonNoRole" >
  <ng-container *appHasPermission="role">
    <!-- button with *appHasPermission -->
    <ng-template *ngTemplateOutlet="buttonNoRole;"></ng-template>
  </ng-container>
</ng-container>

<ng-template #buttonNoRole>
  <!-- button without *appHasPermission -->
  <button
    mat-raised-button type="button"
    [color]="color"
    [disabled]="disabled"
    [(appClickProgress)]="onClick"
    [key]="progressKey">
    <mat-icon *ngIf="icon">{{ icon }}</mat-icon> {{ label }}
  </button>
</ng-template>

Таким образом вы не дублируете <button>код.


0

Да, это возможно.

html с директивой appActiveAhover :)

  <li routerLinkActive="active" #link1="routerLinkActive">
        <a [appActiveAhover]='link1.isActive?false:true' routerLink="administration" [ngStyle]="{'background':link1.isActive?domaindata.get_color3():none}">
          <i class="fa fa-users fa-lg" aria-hidden="true"></i> Administration</a>
      </li>
      <li  routerLinkActive="active" #link2="routerLinkActive">
        <a [appActiveAhover]='link2.isActive?false:true' routerLink="verkaufsburo" [ngStyle]="{'background':link2.isActive?domaindata.get_color3():none,'color':link2.isActive?color2:none}">
          <i class="fa fa-truck fa-lg" aria-hidden="true"></i> Verkaufsbüro</a>
      </li>
      <li  routerLinkActive="active" #link3="routerLinkActive">
        <a [appActiveAhover]='link3.isActive?false:true' routerLink="preisrechner" [ngStyle]="{'background':link3.isActive?domaindata.get_color3():none}">
          <i class="fa fa-calculator fa-lg" aria-hidden="true" *ngIf="routerLinkActive"></i> Preisrechner</a>
      </li>

директива

@Directive({
  selector: '[appActiveAhover]'
})
export class ActiveAhoverDirective implements OnInit {
  @Input() appActiveAhover:boolean;
  constructor(public el: ElementRef, public renderer: Renderer, public domaindata: DomainnameDataService) {
}

  ngOnInit() {
  }

  @HostListener('mouseover') onMouseOver() {
    if(this.appActiveAhover){
      this.renderer.setElementStyle(this.el.nativeElement, 'color', this.domaindata.domaindata.color2);
    }
  }

  @HostListener('mouseout') onMouseOut() {
    if(this.appActiveAhover){
      this.renderer.setElementStyle(this.el.nativeElement, 'color', 'white');
    }
  }

}



-1

Использовать NgClass

[ngClass]="{ 'mat-raised-button': trueCondition }"

пример истинного состояния:

this.element === 'Today'

или логическая функция

getTruth()

полный пример:

  <button [ngClass]="{ 'mat-raised-button': trueCondition }">TEXT</button>

Если вам нужен класс по умолчанию:

  <button [ngClass]="{ 'mat-raised-button': trueCondition, 'default-class': !trueCondition }">TEXT</button>

6
Я не об этом спрашивал. Вы показываете пример классу. Я говорю об атрибуте-директиве.
user2899728

@ user2899728 К сожалению, другого выхода нет. (Вы не можете установить md-raised-buttonатрибут как false)
Эдрик

@ user2899728 Невозможно применить директивы условно. Я попытался дать вам то, что вы искали (конечный результат), с другим подходом («средства»).
Моше

Когда вы предлагаете творческую альтернативу, обычно полезно сначала поставить строку комментариев, чтобы описать, к чему вы идете со своим ответом.
bvdb

1
@bvdb спасибо за добрые слова. Я писал без указания направления, и это было ошибкой. В будущем я надеюсь отредактировать этот пост, так как он может помочь даже одному человеку.
Моше

-1

У меня появилась другая идея о том, что вы могли бы сделать.

Вы можете сохранить HTML-код, который хотите заменить, в переменной в виде строки, а затем добавить / удалить из него директиву bypassSecurityTrustHtmlпо своему усмотрению, используя метод DomSanitizer .

Я не привел к чистому решению, но, по крайней мере, вам не нужно повторять код.


-3

По состоянию на 18 января 2019 года я добавил условную директиву в Angular 5 и выше. Мне нужно было изменить цвет <app-nav>компонента на основе darkMode. Была ли страница в темном режиме или нет.

Это сработало для меня:

<app-nav [color]="darkMode ? 'orange':'green'"></app-nav>

Я надеюсь, что это поможет кому-то.

РЕДАКТИРОВАТЬ

Это изменяет значение атрибута (цвета) в зависимости от условия. Так уж получилось, что цвет определяется с помощью директивы. Итак, любой, кто читает это, пожалуйста, не запутайтесь, это не применяет директиву условно (т.е. что означает добавление или удаление директивы в dom на основе условия)


3
Вы применяете директиву в обоих случаях только с разными параметрами (например, оранжевым или зеленым). Вопрос требует, чтобы директива вообще игнорировалась в зависимости от условия.
Mojtaba
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.