Aplicar una directiva condicionalmente

109

Estoy usando Material 2 para agregar md-raised-button . Quiero aplicar esta directiva solo si cierta condición se cumple.

Por ejemplo:

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

Otro ejemplo: creé una forma reactiva dinámica básica en plunker. Estoy usando una formArrayNamedirectiva de forma reactiva para una matriz de controles. Quiero aplicar la formArrayNamedirectiva solo si una condición específica se cumple; de ​​lo contrario, no agregue la formArrayNamedirectiva.

Aquí hay un enlace de plunker .

usuario2899728
fuente
Sí md-raise-button es una directiva de atributo ( material.angular.io/components/component/button )
user2899728
La aplicación de las condiciones en las que se usan las directivas probablemente haría que AoT fuera inútil, ya que no podría compilar las plantillas a menos que la aplicación se estuviera ejecutando.
Reactgular

Respuestas:

54

No sé si puede aplicar directivas basadas en una condición, pero una solución alternativa sería tener 2 botones y mostrarlos en función de una condición .

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

Editar: tal vez esto sea ​​útil.

LLL
fuente
13
Gracias por su respuesta. Pero ya he usado esta técnica en el ejemplo del plunker que agregué en el detalle de la pregunta. Esta es una buena opción, pero no una buena idea si el código es complejo. Porque porque esta técnica arruina el concepto de reutilización. Puedes ver mi ejemplo en plunker y notar cómo mi código está duplicado debido a este enfoque. Por eso no quiero usar esta técnica. Quiero una mejor solución para poder generar elementos dinámicos con.
user2899728
1
Veo. Puede envolver el código que está duplicado en el componente.
LLL
3
Es una buena idea, pero desafortunadamente no funciona en el caso de formas reactivas. Con las formas reactivas, aparece otro problema. Si pongo la entrada en un componente separado, entonces angular requeriría que agregue [formGroup] = "form" dentro de ese componente secundario también. Pero si lo hago, mi enlace de matriz no funcionaría.
user2899728
15
Esa es una solución terrible ya que es un código duplicado. Imagina que no es solo un botón, sino un div con una gran cantidad de código html dentro.
Shachar Har-Shuv
87

Si solo necesita agregar un atributo para activar las reglas CSS, puede usar el siguiente método: (esto no crea / destruye dinámicamente una directiva)

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

Aplica lo mismo a tu plunker: tenedor

Actualizar:

Cómo condition ? '' : nullfunciona como valor:

Cuando es la cadena vacía ( '') se convierte attr.md-raised-button="", cuando es nullel atributo no existirá.

Actualización: plunker update: fork (problemas de versión solucionados, tenga en cuenta que la pregunta se basó originalmente en angular 4)

Aldracor
fuente
@Luke Sí, si te refieres a "(ahora)" como: desde 2017-01-06, que fue 5 meses antes de que se hiciera la pregunta. ver registro de cambios angular 6to elemento
Aldracor
@Aldracor angular 5.2.1, no funciona. Y no entiendo por qué debería verse como "condición": nulo "donde ambos resultados son básicamente falsos. ¿Qué debería ser en lugar de ''? 'verdadero' tampoco funciona.
Arsenii Fomin
1
así que para cualquiera que no quiera crear un elemento html para que su directiva funcione y esté usando ng-container, simplemente pasé un [habilitado] = "booleanVar"
Ben Taliadoros
7
No me funciona en Angular 6. La muestra de plunker no pasa de la pantalla de carga. Según mi lectura, este enfoque puede funcionar para atributos HTML, pero no funcionará para directivas Angular. La pregunta original era sobre Directivas angulares de la biblioteca de materiales ng.
JeffryHouser
6
@kbpontius gracias por el comentario, funciona en atributos Html pero no en directivas angulares (como atributo)
Reza
19

Como ya se señaló, esto no parece ser posible. Una cosa que se puede utilizar para al menos prevenir algunas duplicaciones es ng-template. Esto le permite extraer el contenido del elemento afectado por la ngIframificación.

Si, por ejemplo, desea crear un componente de menú jerárquico utilizando Material angular:

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

Aquí es la directiva aplicada condicionalmente matMenuTriggerFor, que solo debe aplicarse a elementos de menú con niños. El contenido del botón se inserta en ambos lugares a través de ngTemplateOutlet.

media pensión
fuente
6
Esta es la única respuesta que permite a un desarrollador usar directivas condicionales, así como la reutilización del código.
Ravinder Payal
16

Esto puede llegar tarde, pero es un método viable y elegante para aplicar una directiva de forma condicional.

En la clase directiva, cree la variable de entrada:

@Input('myDirective') options: any;

Al aplicar la directiva, establezca la propiedad de aplicación de la variable de entrada:

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

En el método de la directiva, verifique la variable this.options.apply y aplique la lógica de la directiva basada en la condición:

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

    // directive logic
}
Tiha
fuente
3
Esto no funciona para mí, ya que la directiva todavía existe en el componente, simplemente no realiza ninguna lógica. Me gustaría que la existencia de la directiva se aplique condicionalmente, específicamente porque hay CSS que verifica esta directiva.
Ran Lottem
Tiene un problema diferente al que se enumera aquí (Aplicar la directiva condicionalmente). Publica tu problema y la gente te ayudará. Como primera idea, puede poner la directiva en un div y poner un ng-container con un * ngIf alrededor para aplicar su condición. ngIf elimina el nodo div del DOM, por lo que podría funcionar. Solo estoy adivinando, necesitas publicar tu problema con ejemplos de código / descripción para que las personas te ayuden.
Tiha
Ésta no es una buena solución. La directiva aún se inicializa.
Dino
8

Como también han dicho otros, directivesno se puede aplicar dinámicamente.

Sin embargo, si solo desea cambiar md-buttonel estilo de plano a elevado , este

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

haría el truco. Plunker

Ankit Singh
fuente
3

Esta también podría ser una solución:

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


Funciona para angular 4, ionic 3 como este:

[color]="condition ? 'primary' : ''"donde conditiones una función que decide si esta es una página activa o no. Todo el código se ve así:

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

zalog
fuente
2

Actualmente, existe una NOforma de aplicar condicionalmente una directiva a un componente. Esto no es compatible. Los componentes que ha creado se pueden agregar o eliminar condicionalmente.

Ya existe un problema creado para lo mismo con angular2, por lo que también debería ser el caso con angular4.

Alternativamente, puede optar por la opción con ng-if

<button ngIf="!condition"></button>
<button ngIf="condition" md-raised-button></button> 
Sajeetharan
fuente
1
ya cubierto en la respuesta de LLL
Sumit Ramteke
2

No pude encontrar una buena solución existente, así que construí mi propia directiva que hace esto.

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

Entonces tu html:

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

Cappi10
fuente
Puede usar solo a en @HostBinding('attr.dynamic-attr') @Input('dynamic-attr') attr: string;lugar de ngOnInit + ElementRef.
pinguinjkeke
2
Esta no es una respuesta a la pregunta de cómo agregar una directiva de forma dinámica, no un atributo.
Chris Haines
2

Quizás ayude a alguien.

En el siguiente ejemplo, tengo el my-button.component.htmly quiero aplicar la *appHasPermissiondirectiva al <button>solo si el roleatributo está establecido.

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

De esa forma no duplicarás el <button>código.

MhagnumDw
fuente
0

Sí, es posible.

página html con la directiva 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>

directiva

@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
fuente
2
Como nota al margen, el Renderer está en desuso, en su lugar use Renderer2: alligator.io/angular/using-renderer2
tam.teixeira
0

¡Pasar nulla la directiva lo elimina!

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

Utilizar NgClass

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

ejemplo de condición verdadera:

this.element === 'Today'

o una función booleana

getTruth()

ejemplo completo:

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

Si quieres una clase predeterminada:

  <button [ngClass]="{ 'mat-raised-button': trueCondition, 'default-class': !trueCondition }">TEXT</button>
Moshe
fuente
6
Esto no es lo que pregunté. Estás mostrando un ejemplo para la clase. Estoy hablando de directiva de atributos.
user2899728
@ user2899728 Desafortunadamente, no hay otra forma de hacerlo. (No se puede establecer el md-raised-buttonatributo como falso)
Edric
@ user2899728 No hay forma de aplicar directivas condicionalmente. He intentado darle lo que estaba buscando (resultado final) con un enfoque diferente (los "medios").
Moshe
Cuando ofreces una alternativa creativa, por lo general es útil poner primero una línea de comentarios para describir hacia dónde vas con tu respuesta.
bvdb
1
@bvdb gracias por sus amables palabras. Estaba escribiendo sin rumbo y eso fue un error. En el futuro, espero editar esta publicación, ya que podría ayudar incluso a una persona.
Moshe
-1

Tengo otra idea de lo que podrías hacer.

Puede almacenar el html que desea reemplazar en una variable como una cadena y luego agregar / eliminar la directiva como desee, utilizando el bypassSecurityTrustHtmlmétodo de DomSanitizer .

No obtengo una solución limpia, pero al menos no es necesario que repita el código.

LLL
fuente
-3

Al 18 de enero de 2019, así es como agregué una directiva condicionalmente en Angular 5 y superior. Necesitaba cambiar el color del <app-nav>componente en función de darkMode. Si la página estaba en modo oscuro o no.

Esto funcionó para mí:

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

Espero que esto ayude a alguien.

EDITAR

Esto cambia el valor de un atributo (color) según una condición. Sucede que el color se define mediante una directiva. Entonces, cualquiera que lea esto, no se confunda, esto no está aplicando una directiva condicionalmente (es decir, lo que significa agregar o eliminar una directiva al dom en función de una condición)

Sandra israel
fuente
3
Usted aplica la directiva en ambos casos solo con diferentes opciones (es decir, naranja o verde). La pregunta pide ignorar la directiva en absoluto basándose en una condición.
Mojtaba