Aplicar uma diretiva condicionalmente

109

Estou usando o Material 2 para adicionar md-raised-button. Desejo aplicar esta diretiva apenas se certa condição se tornar verdadeira.

Por exemplo:

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

Outro exemplo: criei uma forma reativa dinâmica básica no plunker. Estou usando uma formArrayNamediretiva de forma reativa para uma série de controles. Quero aplicar a formArrayNamediretiva apenas se uma condição específica se tornar verdadeira, caso contrário, não adicione a formArrayNamediretiva.

Aqui está um link de êmbolo .

user2899728
fonte
Sim, o botão em relevo md é uma diretiva de atributo ( material.angular.io/components/component/button )
user2899728
Aplicar as condições nas quais as diretivas são usadas provavelmente tornaria o AoT inútil, pois você não seria capaz de compilar os modelos a menos que o aplicativo estivesse em execução.
Reagular de

Respostas:

54

Não sei se você pode aplicar diretivas com base em uma condição, mas uma solução alternativa seria ter 2 botões e exibi-los com base em uma condição .

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

Editar: talvez isso seja útil.

EU VOU
fonte
13
Obrigado pela sua resposta. Mas já usei essa técnica no exemplo do êmbolo que adicionei nos detalhes da pergunta. Esta é uma boa opção, mas não é uma boa ideia se o código for complexo. Pois porque essa técnica estraga o conceito de reaproveitamento. Você pode ver meu exemplo no plunker e perceber como meu código é duplicado por causa dessa abordagem. É por isso que não quero usar essa técnica. Eu quero alguma solução melhor para que possa gerar elementos dinâmicos com.
user2899728
1
Entendo. Você pode envolver o código que está duplicado no componente.
LLL de
3
É uma boa ideia, mas infelizmente não funciona no caso de formas reativas. Com as formas reativas, outro problema aparece. Se eu colocar a entrada em um componente separado, o angular exigirá que eu adicione [formGroup] = "formulário" dentro desse componente filho também. Mas se eu fizer isso, minha associação de array não funcionará.
user2899728
15
Essa é uma solução terrível, pois é código duplicado. Imagine que não é apenas um botão, mas um div com muito código html dentro dele.
Shachar Har-Shuv
87

Se você só precisa adicionar um atributo para acionar as regras CSS, pode usar o método a seguir: (isso não cria / destrói dinamicamente uma diretiva)

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

Aplicado o mesmo ao seu êmbolo: garfo

Atualizar:

Como condition ? '' : nullfunciona como o valor:

Quando é a string vazia ( ''), torna-se attr.md-raised-button="", quando é nullo atributo não existirá.

Atualização: atualização do plunker: fork (problemas de versão corrigidos, observe que a questão foi originalmente baseada no angular 4)

Aldracor
fonte
@Luke Sim, se você quer dizer "(agora)" como: desde 06/01/2017, que foi 5 meses antes da pergunta ser feita. ver item 6 do changelog angular
Aldracor
@Aldracor angular 5.2.1, não funciona. E eu nem mesmo entendo por que deveria ser parecido com "condição? '': Null" onde ambos os resultados são basicamente falsos. O que deve ser em vez de ''? 'true' também não funciona.
Arsenii Fomin
1
então, para quem não deseja criar um elemento html para que sua diretiva funcione e está usando o ng-container, apenas passei um [enabled] = "booleanVar"
Ben Taliadoros
7
Não funciona para mim no Angular 6. A amostra do êmbolo não passa da tela de carregamento. Com base na minha leitura, essa abordagem pode funcionar para atributos HTML, mas não funcionará para diretivas Angular. A pergunta original era sobre as diretivas angulares da biblioteca de materiais ng.
JeffryHouser
6
@kbpontius obrigado pelo comentário, funciona com atributos Html, mas não com diretivas angulares (como atributo)
Reza
19

Como já foi observado, isso não parece ser possível. Uma coisa que pode ser usada para evitar pelo menos alguma duplicação é ng-template. Isso permite que você extraia o conteúdo do elemento afetado pela ngIframificação.

Se você, por exemplo, deseja criar um componente de menu hierárquico usando 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>

Aqui, a diretiva aplicada condicionalmente é matMenuTriggerFor, que só deve ser aplicada a itens de menu com filhos. O conteúdo do botão é inserido em ambos os lugares via ngTemplateOutlet.

HB
fonte
6
Esta é a única resposta que permite ao desenvolvedor usar diretivas condicionais, bem como reutilização de código.
Ravinder Payal
16

Isso pode chegar tarde, mas é um método viável e elegante para aplicar uma diretiva condicionalmente.

Na classe de diretiva, crie a variável de entrada:

@Input('myDirective') options: any;

Ao aplicar a diretiva, defina a propriedade apply da variável de entrada:

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

No método da diretiva, verifique a variável this.options.apply e aplique a lógica da diretiva com base na condição:

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

    // directive logic
}
Tiha
fonte
3
Isso não funciona para mim, como a diretiva ainda existe no componente, ela simplesmente não executa nenhuma lógica. Eu gostaria que a existência da diretiva fosse aplicada condicionalmente, especificamente porque há css que verifica essa diretiva.
Ran Lottem
Você tem um problema diferente do listado aqui (aplique a diretiva condicionalmente). Publique o seu problema e as pessoas irão ajudá-lo. Como uma primeira idéia, você poderia colocar a diretiva em um div e colocar um ng-container com um * ngIf ao redor para aplicar sua condição. ngIf remove o nó div do DOM, então pode funcionar. Estou apenas supondo, você precisa postar seu problema com exemplos de código / descrição para ppl para ajudá-lo.
Tiha
Esta não é uma boa solução. A diretiva ainda foi inicializada.
Dino,
8

Como outros também afirmaram, directivesnão pode ser aplicado dinamicamente.

No entanto, se você quiser apenas alternar md-buttono estilo de plano para elevado , então

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

faria o truque. Plunker

Ankit Singh
fonte
3

Esta também pode ser uma solução:

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


Está funcionando para angular 4, iônico 3 assim:

[color]="condition ? 'primary' : ''"onde conditioné uma função que decide se esta é uma página ativa ou não. Todo o código se parece com isto:

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

zalog
fonte
2

Atualmente, existe uma NOmaneira de aplicar condicionalmente uma diretiva a um componente. Isso não é suportado. Os componentes que você criou podem ser adicionados ou removidos condicionalmente.

Já existe um problema criado para o mesmo com angular2, então deve ser o caso com angular4 também.

Alternativamente, você pode escolher a opção com ng-if

<button ngIf="!condition"></button>
<button ngIf="condition" md-raised-button></button> 
Sajeetharan
fonte
1
já abordado na resposta da LLL
Sumit Ramteke
2

Não consegui encontrar uma boa solução existente, então criei minha própria diretiva que faz isso.

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

Então seu html:

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

Cappi10
fonte
Você pode usar apenas um em @HostBinding('attr.dynamic-attr') @Input('dynamic-attr') attr: string;vez de ngOnInit + ElementRef.
pinguinjkeke
2
Esta não é uma resposta à questão de como adicionar uma diretiva dinamicamente, não um atributo.
Chris Haines
2

Talvez ajude alguém.

No exemplo abaixo, tenho o my-button.component.htmle desejo aplicar a *appHasPermissiondiretiva ao <button>somente se o roleatributo estiver definido.

<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>

Dessa forma, você não duplica o <button>código.

MhagnumDw
fonte
0

Sim, é possível.

página html com diretiva 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>

diretriz

@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');
    }
  }

}
Chaitanya Nekkalapudi
fonte
2
Como uma observação lateral, o Renderer está obsoleto, em vez disso use Renderer2: alligator.io/angular/using-renderer2
tam.teixeira
0

Passar nullpara a diretiva remove isso!

<button md-raised-button="condition ? true : null"></button>
Yogesh Aggarwal
fonte
-1

Usar NgClass

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

exemplo de condição verdadeira:

this.element === 'Today'

ou uma função booleana

getTruth()

exemplo completo:

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

Se você quiser uma classe padrão:

  <button [ngClass]="{ 'mat-raised-button': trueCondition, 'default-class': !trueCondition }">TEXT</button>
Moshe
fonte
6
Não foi isso que perguntei. Você está dando exemplo para a aula. Estou falando sobre diretiva de atributo.
user2899728
@ user2899728 Infelizmente não há outra maneira de fazer isso. (Você não pode definir o md-raised-buttonatributo como falso)
Edric
@ user2899728 Não há como aplicar diretivas condicionalmente. Tentei dar a você o que você estava procurando (resultado final) com uma abordagem diferente (os "meios").
Moshe
Quando você oferece uma alternativa criativa, geralmente ajuda colocar uma linha de comentários primeiro para descrever aonde você quer chegar com sua resposta.
bvdb
1
@bvdb obrigado por suas amáveis ​​palavras. Eu estava escrevendo sem direção e isso foi um erro. No futuro, espero editar este post, pois pode ajudar até mesmo uma pessoa.
Moshe de
-1

Tive outra ideia sobre o que você poderia fazer.

Você pode armazenar o html que deseja substituir em uma variável como uma string e, em seguida, adicionar / remover a diretiva como desejar, usando o bypassSecurityTrustHtmlmétodo do DomSanitizer .

I não resulta em uma solução limpa, mas pelo menos você não precisa repetir o código.

EU VOU
fonte
-3

Em 18 de janeiro de 2019, foi assim que adicionei uma diretiva condicionalmente no Angular 5 e acima. Eu precisava mudar a cor do <app-nav>componente com base em darkMode. Se a página estava no modo escuro ou não.

Isso funcionou para mim:

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

Espero que isso ajude alguém.

EDITAR

Isso altera o valor de um atributo (cor) com base em uma condição. Acontece que a cor é definida por meio de uma diretiva. Portanto, quem estiver lendo isso, não se confunda, isso não é aplicar uma diretiva condicionalmente (ou seja, o que significa adicionar ou remover uma diretiva ao dom com base em uma condição)

Sandra israel
fonte
3
Você aplica a diretiva em ambos os casos apenas com opções diferentes (ou seja, laranja ou verde). A questão pede para ignorar a diretiva em tudo com base em uma condição.
Mojtaba,