¿Cuál es la mejor manera de aplicar atributos condicionalmente en AngularJS?

296

Necesito poder agregar, por ejemplo, "contenteditable" a los elementos, en función de una variable booleana en el alcance.

Ejemplo de uso:

<h1 attrs="{'contenteditable=\"true\"': editMode}">{{content.title}}</h1>

Resultaría en contenteditable = true que se agrega al elemento si $scope.editModese estableció en true. ¿Hay alguna manera fácil de implementar este comportamiento de atributo de clase ng? Estoy considerando escribir una directiva y compartirla si no.

Editar: Puedo ver que parece haber algunas similitudes entre mi directiva attrs propuesta y ng-bind-attrs, pero se eliminó en 1.0.0.rc3 , ¿por qué?

Kenneth Lynne
fuente
2
No he encontrado nada como eso. Yo voto ¡hazlo! : D Quizás lo envíe al proyecto angular. Una sintaxis que coincida mejor con la clase ng sería buena. ng-attr="expression".
Xesued
Definitivamente estoy considerando que sí!
Kenneth Lynne
3
Esto realmente no es lo mismo que ngClass o ngStyle porque controlan un solo atributo y usted desea controlar cualquier atributo. Creo que sería mejor crear una contentEditabledirectiva.
Josh David Miller
@JoshDavidMiller +1 en eso. Creo que hay un pequeño beneficio para poder controlar cualquier atributo, especialmente teniendo en cuenta la complejidad. Puede tener directivas condicionalmente actuando sobre una variable.
Umur Kontacı
<h1 ng-attr-contenteditable="{{editMode && true : false}}">{{content.title}}</h1>
mia

Respuestas:

272

Estoy usando lo siguiente para establecer condicionalmente la clase attr cuando no se puede usar ng-class (por ejemplo, al diseñar SVG):

ng-attr-class="{{someBoolean && 'class-when-true' || 'class-when-false' }}"

El mismo enfoque debería funcionar para otros tipos de atributos.

(Creo que necesitas estar en el último Angular inestable para usar ng-attr-, actualmente estoy en 1.1.4)

Ashley Davis
fuente
97
Solo para aclarar esta respuesta: si prefijas cualquier atributo con ng-attr-, entonces el compilador eliminará el prefijo y agregará el atributo con su valor vinculado al resultado de la expresión angular del valor del atributo original.
Matthias
12
En mi opinión, es más fácil de leer si usa un operador ternario, para el cual se agregó soporte en 1.1.5. Eso se vería así: {{someConditionalExpression? 'class-when-true': 'class-when-false'}}
dshap
129129
El problema con este enfoque es que el atributo se crea independientemente del resultado. Idealmente, nos gustaría controlar si el atributo se crea o no.
airtonix
24
Tenga en cuenta que ng-attr- stuff se eliminó de Angular 1.2. Esta respuesta ya no es válida.
Judá Gabriel Himango
2
Está usted equivocado. Este proyecto github.com/codecapers/AngularJS-FlowChart usa Angular 1.2 y ng-attr-. También los últimos documentos (Angular 1.4) todavía incluyen ng-attr-
Ashley Davis el
120

Puede prefijar atributos con ng-attrpara evaluar una expresión angular. Cuando el resultado de las expresiones no está definido, esto elimina el valor del atributo.

<a ng-attr-href="{{value || undefined}}">Hello World</a>

Producirá (cuando el valor es falso)

<a ng-attr-href="{{value || undefined}}" href>Hello World</a>

Así que no lo use falseporque eso producirá la palabra "falso" como valor.

<a ng-attr-href="{{value || false}}" href="false">Hello World</a>

Al usar este truco en una directiva. Los atributos de la directiva serán falsos si les falta un valor.

Por ejemplo, lo anterior sería falso.

function post($scope, $el, $attr) {
      var url = $attr['href'] || false;
      alert(url === false);
}
Reactgular
fuente
3
Me gusta esta respuesta Sin embargo, no creo que si el valor no esté definido, ocultará el atributo. Aunque podría estar perdiendo algo. Entonces, el primer resultado sería <a ng-attr-href="{{value || undefined}}" href> Hola Mundo </a>
Roman K K
13
@RomanK hola, el manual dice que undefinedes un caso especial. "Cuando se usa ngAttr, se usa el indicador allOrNothing de $ interpolate, por lo que si alguna expresión en la cadena interpolada resulta indefinida, el atributo se elimina y no se agrega al elemento".
Reactgular
99
Solo una nota para ayudar a futuros lectores, el comportamiento 'indefinido' parece haberse agregado en Angular 1.3. Estoy usando 1.2.27 y actualmente debo IE8.
Adam Marshall
3
Para confirmar un poco más, Angular 1.2.15 mostrará href incluso si el valor no está definido, parece que el comportamiento indefinido comienza en Angular 1.3 como dice el comentario anterior.
Brian Ogden
Es importante señalar que ng-attr-fooseguirá siendo siempre invocar la foodirectiva si se posee, aunque ng-attr-fooevalúa a undefined. discusión
hughes
97

Obtuve esto trabajando configurando el atributo. Y controlar la aplicabilidad del atributo utilizando el valor booleano para el atributo.

Aquí está el fragmento de código:

<div contenteditable="{{ condition ? 'true' : 'false'}}"></div>

Espero que esto ayude.

jsbisht
fuente
Como angular puede analizar la expresión IIF, esto funciona perfectamente para evaluar el estado en el alcance actual y asignar valores basados ​​en ese estado.
mccainz
22

En la última versión de Angular (1.1.5), han incluido una directiva condicional llamada ngIf. Es diferente de ngShowy ngHideen que los elementos no están ocultos, pero no están incluidos en el DOM. Son muy útiles para componentes que son costosos de crear pero que no se usan:

<h1 ng-if="editMode" contenteditable=true>{{content.title}}</h1>
Brian Genisio
fuente
32
Creo que ng-if solo funciona en un nivel de elemento, es decir, no puede especificar un condicional a solo un atributo de un elemento.
Max Strater
3
De hecho, pero luego puedes hacerlo<h1 ng-if="editMode" contenteditable="true"><h1 ng-if="!editMode">
ByScripts
55
cuidado, sin embargo, eso ng-ifcrea un nuevo alcance.
Shimon Rachlenko
1
@ShimonRachlenko De acuerdo! Esto puede ser una gran fuente de confusión y errores. ng-ifcrea un nuevo alcance pero ng-showno lo hace. Esta inconsistencia siempre ha sido un punto doloroso para mí. La reacción de programación defensiva a esto es: "siempre se une a algo que es un punto en la expresión" y no será un problema.
Brian Genisio el
Si desea agregar un atributo que en realidad es una directiva, esto funciona muy bien. Es una pena la duplicación del código
Adam Marshall
16

Para obtener un atributo que muestre un valor específico basado en una verificación booleana, o que se omita por completo si la verificación booleana falla, utilicé lo siguiente:

ng-attr-example="{{params.type == 'test' ? 'itWasTest' : undefined }}"

Ejemplo de uso:

<div ng-attr-class="{{params.type == 'test' ? 'itWasTest' : undefined }}">

Saldría <div class="itWasTest">o <div>basado en el valor deparams.type

Cañada
fuente
9

<h1 ng-attr-contenteditable="{{isTrue || undefined }}">{{content.title}}</h1>

producirá cuando isTrue = true: <h1 contenteditable="true">{{content.title}}</h1>

y cuando isTrue = false: <h1>{{content.title}}</h1>

Garfi
fuente
55
¿Qué pasa si quiero algo como <h1 contenteditable = "row">
SuperUberDuper
<h1 ng-attr-contenteditable="{{isTrue ? 'row' : undefined}}">{{content.title}} </h1>
PhatBuck
Esta debería ser la respuesta aceptada. Mi escenario era<video ng-attr-autoplay="{{vm.autoplay || undefined}}" />
LucasM
6

Con respecto a la solución aceptada, la publicada por Ashley Davis, el método descrito todavía imprime el atributo en el DOM, independientemente del hecho de que el valor que se le ha asignado no está definido.

Por ejemplo, en una configuración de campo de entrada con un modelo ng y un atributo de valor:

<input type="text" name="myInput" data-ng-attr-value="{{myValue}}" data-ng-model="myModel" />

Independientemente de lo que esté detrás de myValue, el atributo de valor todavía se imprime en el DOM, por lo tanto, se interpreta. Ng-model entonces, se anula.

Un poco desagradable, pero usar ng-if funciona:

<input type="text" name="myInput" data-ng-if="value" data-ng-attr-value="{{myValue}}" data-ng-model="myModel" />
<input type="text" name="myInput" data-ng-if="!value" data-ng-model="myModel" />

Recomendaría usar una verificación más detallada dentro de las directivas ng-if :)

alexc
fuente
Sin embargo, con este enfoque, repetirá el mismo código.
Kalyan
Es cierto, pero mantiene segura la declaración del modelo. Mi problema con el uso de la solución aceptada fue que el atributo de valor terminó en el DOM, prevaleciendo sobre la declaración del modelo. Por lo tanto, si el atributo de valor está vacío pero el modelo no lo está, se anulará para tomar un valor vacío.
alexc
6

También puedes usar una expresión como esta:

<h1 ng-attr-contenteditable="{{ editMode ? true : false }}"></h1>
Kamuran Sönecek
fuente
5

De hecho, escribí un parche para hacer esto hace unos meses (después de que alguien me lo preguntó en #angularjs en freenode).

Probablemente no se fusionará, pero es muy similar a ngClass: https://github.com/angular/angular.js/pull/4269

Ya sea que se fusione o no, el material ng-attr- * existente probablemente sea adecuado para sus necesidades (como otros han mencionado), aunque podría ser un poco más complicado que la funcionalidad más ngClass-style que está sugiriendo.

crónica
fuente
2

Para la validación del campo de entrada puede hacer:

<input ng-model="discount" type="number" ng-attr-max="{{discountType == '%' ? 100 : undefined}}">

Esto se aplicará el atributo maxde 100sólo si discountTypese define como%

Nestor Britez
fuente
1

Editar : ¡Esta respuesta está relacionada con Angular2 +! Lo siento, me perdí la etiqueta!

Respuesta original:

En cuanto al caso muy simple cuando solo desea aplicar (o establecer) un atributo si se estableció un cierto valor de entrada, es tan fácil como

<my-element [conditionalAttr]="optionalValue || false">

Es lo mismo que:

<my-element [conditionalAttr]="optionalValue ? optionalValue : false">

(Por lo tanto, opcionalValue se aplica si se da lo contrario, la expresión es falsa y el atributo no se aplica).

Ejemplo: tuve el caso, donde dejé aplicar un color pero también estilos arbitrarios, donde el atributo de color no funcionaba como ya estaba establecido (incluso si no se proporcionó el color @Input ()):

@Component({
  selector: "rb-icon",
  styleUrls: ["icon.component.scss"],
  template: "<span class="ic-{{icon}}" [style.color]="color==color" [ngStyle]="styleObj" ></span>",
})
export class IconComponent {
   @Input() icon: string;
   @Input() color: string;
   @Input() styles: string;

   private styleObj: object;
...
}

Por lo tanto, "style.color" solo se estableció cuando el atributo de color estaba allí; de lo contrario, se podría usar el atributo de color en la cadena "styles".

Por supuesto, esto también podría lograrse con

[style.color]="color" 

y

@Input color: (string | boolean) = false;
Zafoide
fuente
Angular.JS es Angular 1, que es muy diferente de Angular 2+. Su solución responde a Angular 2+, pero se solicitó AngularJS (1).
ssc-hrep3
@ ssc-hrep3: Tienes razón. Lo siento, me perdí eso! Gracias. Edité la respuesta para señalar eso. :-)
Zaphoid
es angularjs no angular, angularjs es angular 1
dgzornoza
0

En caso de que necesite una solución para Angular 2, entonces es simple, use el enlace de propiedades como se muestra a continuación, por ejemplo, si desea que la entrada se lea solo condicionalmente, luego agregue entre corchetes el atributo seguido de = signo y expresión.

<input [readonly]="mode=='VIEW'"> 
Sanjay Singh
fuente
es angularjs (angular1) no angular (angular2)
dgzornoza
angular 2+ y Angular JS no son lo mismo, son más bien productos completamente diferentes.
Sanjay Singh
0

Pude hacer que esto funcionara:

ng-attr-aria-current="{{::item.isSelected==true ? 'page' : undefined}}"

Lo bueno aquí es que si item.isSelected es falso, entonces el atributo simplemente no se representa.

Richard Strickland
fuente