Quelle est la meilleure façon d'appliquer conditionnellement des attributs dans AngularJS?

296

Je dois pouvoir ajouter par exemple "contenteditable" aux éléments, basé sur une variable booléenne sur la portée.

Exemple d'utilisation:

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

Cela entraînerait l'ajout de contenteditable = true à l'élément s'il $scope.editModeétait défini sur true. Existe-t-il un moyen simple d'implémenter ce comportement d'attribut de classe ng? J'envisage d'écrire une directive et de partager sinon.

Edit: Je peux voir qu'il semble y avoir des similitudes entre ma directive attrs proposée et ng-bind-attrs, mais elle a été supprimée dans 1.0.0.rc3 , pourquoi?

Kenneth Lynne
la source
2
Je n'ai rien vu de tel. Je vote fais-le! : D Peut-être le soumettre au projet angulaire. Une syntaxe qui correspond mieux à la classe ng serait bien. ng-attr="expression".
Xesued
Je pense définitivement que oui!
Kenneth Lynne
3
Ce n'est vraiment pas la même chose que ngClass ou ngStyle car ils contrôlent un seul attribut et vous voulez contrôler n'importe quel attribut. Je pense qu'il serait préférable de créer une contentEditabledirective.
Josh David Miller
@JoshDavidMiller +1 à ce sujet. Je pense qu'il y a un petit avantage à pouvoir contrôler n'importe quel attribut, surtout compte tenu de la complexité. Vous pouvez avoir des directives agissant conditionnellement sur une variable.
Umur Kontacı
<h1 ng-attr-contenteditable="{{editMode && true : false}}">{{content.title}}</h1>
mia

Réponses:

272

J'utilise ce qui suit pour définir conditionnellement la classe attr lorsque la classe ng ne peut pas être utilisée (par exemple lors du style SVG):

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

La même approche devrait fonctionner pour d'autres types d'attributs.

(Je pense que vous devez être sur le dernier Angular instable pour utiliser ng-attr-, je suis actuellement sur 1.1.4)

Ashley Davis
la source
97
Juste pour clarifier cette réponse: si vous préfixez un attribut avec ng-attr-, le compilateur supprimera le préfixe et ajoutera l'attribut avec sa valeur liée au résultat de l' expression angulaire à partir de la valeur d'attribut d'origine.
Matthias
12
À mon avis, il est plus facile à lire si vous utilisez un opérateur ternaire, pour lequel le support a été ajouté au 1.1.5. Cela ressemblerait à: {{someConditionalExpression? 'class-when-true': 'class-when-false'}}
dshap
129
le problème avec cette approche est que l'attribut est créé quel que soit le résultat. Idéalement, nous aimerions contrôler si l'attribut est créé ou non.
airtonix
24
Notez que le ng-attr-stuff a été supprimé d'Angular 1.2. Cette réponse n'est plus valable.
Judah Gabriel Himango
2
Vous avez tort. Ce projet github.com/codecapers/AngularJS-FlowChart utilise Angular 1.2 et ng-attr-. De plus, les derniers documents (Angular 1.4) incluent toujours ng-attr-
Ashley Davis
120

Vous pouvez préfixer les attributs avec ng-attrpour évaluer une expression angulaire. Lorsque le résultat des expressions n'est pas défini, cela supprime la valeur de l'attribut.

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

Produira (lorsque la valeur est fausse)

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

Donc, ne l'utilisez pas falsecar cela produira le mot "faux" comme valeur.

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

Lorsque vous utilisez cette astuce dans une directive. Les attributs de la directive seront faux s'il leur manque une valeur.

Par exemple, ce qui précède serait faux.

function post($scope, $el, $attr) {
      var url = $attr['href'] || false;
      alert(url === false);
}
Reactgular
la source
3
J'aime cette réponse. Cependant, je ne pense pas que si la valeur n'est pas définie, elle masquera l'attribut. Il se peut que je manque quelque chose. Le premier résultat serait donc <a ng-attr-href="{{value || undefined}}" href> Hello World </a>
Roman K
13
@RomanK salut, le manuel indique qu'il undefineds'agit d'un cas spécial. "Lorsque vous utilisez ngAttr, l'indicateur allOrNothing de $ interpolate est utilisé, donc si une expression de la chaîne interpolée se traduit par undefined, l'attribut est supprimé et non ajouté à l'élément."
Reactgular
9
Juste une note pour aider les futurs lecteurs, le comportement «non défini» semble avoir été ajouté dans Angular 1.3. J'utilise 1.2.27 et je dois actuellement IE8.
Adam Marshall
3
Pour confirmer un peu plus Angular 1.2.15 affichera href même si la valeur n'est pas définie, il semble que le comportement non défini commence dans Angular 1.3 comme le déclare le commentaire ci-dessus.
Brian Ogden
Il est important de noter que ng-attr-foo sera toujours toujours invoquer la foodirective si elle existe, même ng-attr-fooévalue à undefined. discussion
Hugh
97

J'ai obtenu ce travail en définissant dur l'attribut. Et contrôler l'applicabilité de l'attribut à l'aide de la valeur booléenne de l'attribut.

Voici l'extrait de code:

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

J'espère que ça aide.

jsbisht
la source
Comme angulaire peut analyser l'expression IIF, cela fonctionne parfaitement pour évaluer l'état dans la portée actuelle et attribuer des valeurs basées sur cet état.
mccainz
22

Dans la dernière version d'Angular (1.1.5), ils ont inclus une directive conditionnelle appelée ngIf. Il est différent ngShowet ngHideen ce sens que les éléments ne sont pas cachés, mais pas du tout inclus dans le DOM. Ils sont très utiles pour les composants dont la création est coûteuse mais qui ne sont pas utilisés:

<h1 ng-if="editMode" contenteditable=true>{{content.title}}</h1>
Brian Genisio
la source
32
Je crois que ng-if ne fonctionne qu'au niveau d'un élément, c'est-à-dire que vous ne pouvez pas spécifier un conditionnel à un seul attribut d'un élément.
Max Strater
3
En effet, mais vous pouvez alors le faire<h1 ng-if="editMode" contenteditable="true"><h1 ng-if="!editMode">
ByScripts
5
attention cependant, cela ng-ifcrée une nouvelle portée.
Shimon Rachlenko
1
@ShimonRachlenko D'accord! Cela peut être une grande source de confusion et de bugs. ng-ifcrée une nouvelle portée mais ng-showne le fait pas. Cette incohérence a toujours été un point sensible pour moi. La réaction de programmation défensive à ceci est: "toujours lier à quelque chose qu'un point dans l'expression" et ce ne sera pas un problème.
Brian Genisio
Si vous souhaitez ajouter un attribut qui est en fait une directive, cela fonctionne très bien. Dommage pour la duplication de code
Adam Marshall
16

Pour obtenir un attribut pour afficher une valeur spécifique basée sur une vérification booléenne, ou être complètement omis si la vérification booléenne a échoué, j'ai utilisé ce qui suit:

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

Exemple d'utilisation:

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

Produirait <div class="itWasTest">ou en <div>fonction de la valeur deparams.type

Glen
la source
9

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

produira lorsque isTrue = true: <h1 contenteditable="true">{{content.title}}</h1>

et quand isTrue = false: <h1>{{content.title}}</h1>

Garfi
la source
5
Et si je veux quelque chose comme <h1 contenteditable = "row">
SuperUberDuper
<h1 ng-attr-contenteditable="{{isTrue ? 'row' : undefined}}">{{content.title}} </h1>
PhatBuck
Cela devrait être la réponse acceptée. Mon scénario était<video ng-attr-autoplay="{{vm.autoplay || undefined}}" />
LucasM
6

En ce qui concerne la solution acceptée, celle publiée par Ashley Davis, la méthode décrite imprime toujours l'attribut dans le DOM, indépendamment du fait que la valeur qui lui a été affectée n'est pas définie.

Par exemple, sur une configuration de champ de saisie avec à la fois un modèle ng et un attribut value:

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

Peu importe ce qui se cache derrière myValue, la valeur attribut toujours imprimé dans le DOM, donc interprété. Ng-model devient alors annulé.

Un peu désagréable, mais utiliser ng-if fait l'affaire:

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

Je recommanderais d'utiliser une vérification plus détaillée à l'intérieur des directives ng-if :)

alexc
la source
Avec cette approche, vous répéterez cependant le même code.
Kalyan
Vrai, mais il garde la déclaration de modèle en sécurité. Mon problème avec l'utilisation de la solution acceptée était que l'attribut value se retrouvait dans le DOM, prenant le pas sur la déclaration du modèle. Ainsi, si l'attribut value est vide mais que le modèle ne l'est pas, le modèle sera remplacé pour prendre une valeur vide.
alexc
6

Vous pouvez également utiliser une expression comme celle-ci:

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

J'ai en fait écrit un patch pour le faire il y a quelques mois (après que quelqu'un l'ait demandé dans #angularjs sur freenode).

Il ne sera probablement pas fusionné, mais il est très similaire à ngClass: https://github.com/angular/angular.js/pull/4269

Qu'il soit fusionné ou non, le contenu ng-attr- * existant convient probablement à vos besoins (comme d'autres l'ont mentionné), bien qu'il puisse être un peu plus maladroit que la fonctionnalité de style plus ngClass que vous proposez.

chronicsalsa
la source
2

Pour la validation du champ de saisie, vous pouvez faire:

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

Il en sera de l'attribut maxà 100seulement si discountTypeest défini comme%

Nestor Britez
la source
1

Edit : Cette réponse est liée à Angular2 +! Désolé, j'ai raté le tag!

Réponse originale:

Quant au cas très simple où vous ne souhaitez appliquer (ou définir) un attribut que si une certaine valeur d'entrée a été définie, c'est aussi simple que

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

C'est la même chose que:

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

(Ainsi, optionalValue est appliqué s'il est donné, sinon l'expression est fausse et l'attribut n'est pas appliqué.)

Exemple: j'ai eu le cas, où j'ai laissé appliquer une couleur mais aussi des styles arbitraires, où l'attribut color ne fonctionnait pas car il était déjà défini (même si la couleur @Input () n'était pas donnée):

@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;
...
}

Ainsi, "style.color" n'a été défini que lorsque l'attribut color était présent, sinon l'attribut color de la chaîne "styles" pourrait être utilisé.

Bien sûr, cela pourrait également être réalisé avec

[style.color]="color" 

et

@Input color: (string | boolean) = false;
Zaphoid
la source
Angular.JS est Angular 1, ce qui est très différent d'Angular 2+. Votre solution répond pour Angular 2+, mais AngularJS (1) a été demandé.
ssc-hrep3
@ ssc-hrep3: Vous avez raison. Désolé d'avoir raté ça! Merci. J'ai édité la réponse pour le souligner. :-)
Zaphoid
est angularjs pas angulaire, angularjs est angulaire 1
dgzornoza
0

Juste au cas où vous auriez besoin d'une solution pour Angular 2, puis sa simple, utilisez la liaison de propriété comme ci-dessous, par exemple, vous voulez faire une entrée en lecture conditionnelle, puis ajoutez des accolades carrées l'attrbute suivi de = signe et expression.

<input [readonly]="mode=='VIEW'"> 
Sanjay Singh
la source
est angularjs (angular1) pas angulaire (angular2)
dgzornoza
angular 2+ et Angular JS ne sont pas identiques, ils ressemblent davantage à des produits différents.
Sanjay Singh
0

A pu faire fonctionner ceci:

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

La bonne chose ici est que si item.isSelected est faux, l'attribut n'est tout simplement pas rendu.

Richard Strickland
la source