Qual é a melhor maneira de aplicar condicionalmente atributos no AngularJS?

296

Eu preciso ser capaz de adicionar, por exemplo, "contenteditable" aos elementos, com base em uma variável booleana no escopo.

Exemplo de uso:

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

Resultaria na adição de contenteditable = true ao elemento se $scope.editModeestivesse definido como true. Existe alguma maneira fácil de implementar esse comportamento de atributo da classe ng? Estou pensando em escrever uma diretiva e compartilhar, se não.

Edit: Eu posso ver que parece haver algumas semelhanças entre minha diretiva attrs proposta e ng-bind-attrs, mas ela foi removida em 1.0.0.rc3 , por que?

Kenneth Lynne
fonte
2
Eu não encontrei nada assim. Eu voto fazê-lo! : D Talvez enviá-lo para o projeto angular. Uma sintaxe que melhor corresponda à classe ng seria legal. ng-attr="expression".
Xesued 29/03
Estou definitivamente considerando que sim!
precisa
3
Isso realmente não é o mesmo que ngClass ou ngStyle, porque eles controlam um único atributo e você deseja controlar qualquer atributo. Eu acho que seria melhor criar uma contentEditablediretiva.
Josh David Miller
@JoshDavidMiller +1 nisso. Eu acho que há um pequeno benefício em poder controlar qualquer atributo, especialmente considerando a complexidade. Você pode ter diretivas agindo condicionalmente em uma variável.
Umur Kontacı
<h1 ng-attr-contenteditable="{{editMode && true : false}}">{{content.title}}</h1>
mia

Respostas:

272

Estou usando o seguinte para definir condicionalmente a classe attr quando a classe ng não puder ser usada (por exemplo, ao estilizar SVG):

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

A mesma abordagem deve funcionar para outros tipos de atributos.

(Eu acho que você precisa estar no mais recente Angular instável para usar ng-attr-, atualmente estou no 1.1.4)

Ashley Davis
fonte
97
Apenas para esclarecer esta resposta: Se você prefixar qualquer atributo com ng-attr-, o compilador retirará o prefixo e adicionará o atributo com seu valor vinculado ao resultado da expressão angular do valor do atributo original.
Matthias
12
Na minha opinião, é mais fácil ler se você usar um operador ternário, para o qual o suporte foi adicionado na 1.1.5. Seria assim: {{someConditionalExpression? 'class-when-true': 'class-when-false'}}
dshap
129
o problema com essa abordagem é que o atributo é criado independentemente do resultado. Idealmente, gostaríamos de controlar se o atributo é ou não criado.
Airtonix 19/05
24
Observe que o ng-attr-stuff foi removido do Angular 1.2. Essa resposta não é mais válida.
Judah Gabriel Himango
2
Você está errado. Este projeto github.com/codecapers/AngularJS-FlowChart usa Angular 1.2 e ng-attr-. Além disso os últimos documentos (angulares 1,4) ainda incluem NG-attr-
Ashley Davis
120

Você pode prefixar atributos com ng-attrpara avaliar uma expressão Angular. Quando o resultado das expressões indefinidas, isso remove o valor do atributo

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

Produzirá (quando o valor for falso)

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

Portanto, não use, falseporque isso produzirá a palavra "false" como o valor.

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

Ao usar esse truque em uma diretiva. Os atributos para a diretiva serão falsos se estiverem faltando um valor.

Por exemplo, o acima seria falso.

function post($scope, $el, $attr) {
      var url = $attr['href'] || false;
      alert(url === false);
}
Reactgular
fonte
3
Eu gosto desta resposta. No entanto, eu não acho que se o valor for indefinido, ele ocultará o atributo. Talvez eu esteja perdendo alguma coisa. Portanto, o primeiro resultado seria <a ng-attr-href="{{value || undefined}}" href> Olá Mundo </a>
Roman K
13
@RomanK oi, o manual declara que undefinedé um caso especial. "Ao usar ngAttr, o sinalizador allOrNothing de $ interpolate é usado; portanto, se qualquer expressão na sequência interpolada resultar em indefinida, o atributo será removido e não será adicionado ao elemento."
Reactgular
9
Apenas uma observação para ajudar futuros leitores, o comportamento "indefinido" parece ter sido adicionado no Angular 1.3. Estou usando o 1.2.27 e atualmente preciso o IE8.
Adam Marshall
3
Para confirmar um pouco mais, o Angular 1.2.15 exibirá href mesmo se o valor for indefinido, parece que o comportamento indefinido começa no Angular 1.3, como afirma o comentário acima.
Brian Ogden
É importante observar que ng-attr-fooainda sempre invocará a foodiretiva, se existir, até ng-attr-fooavalia como undefined. discussão
hughes
97

Eu consegui isso trabalhando definindo muito bem o atributo. E controlando a aplicabilidade do atributo usando o valor booleano do atributo.

Aqui está o trecho de código:

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

Eu espero que isso ajude.

jsbisht
fonte
Como angular pode analisar a expressão IIF, isso funciona perfeitamente para avaliar o estado no escopo atual e atribuir valores com base nesse estado.
22615 mcainz
22

Na versão mais recente do Angular (1.1.5), eles incluíram uma diretiva condicional chamada ngIf. É diferente ngShowe ngHideporque os elementos não estão ocultos, mas não estão incluídos no DOM. Eles são muito úteis para componentes que são caros de criar, mas não são usados:

<h1 ng-if="editMode" contenteditable=true>{{content.title}}</h1>
Brian Genisio
fonte
32
Acredito que ng-se funcionar apenas em um nível de elemento, ou seja, você não poderá especificar uma condicional para apenas um atributo de um elemento.
precisa
3
Na verdade, mas você pode então fazer<h1 ng-if="editMode" contenteditable="true"><h1 ng-if="!editMode">
ByScripts
5
cuidado, porém, que ng-ifcria um novo escopo.
Shimon Rachlenko
1
@ShimonRachlenko Agreed! Isso pode ser uma grande fonte de confusão e bugs. ng-ifcria um novo escopo, mas ng-shownão. Essa inconsistência sempre foi um ponto dolorido para mim. A reação defensiva da programação a isso é: "sempre ligue a algo que seja um ponto na expressão" e isso não será um problema.
9139 Brian Genisio
Se você deseja adicionar um atributo que seja realmente uma diretiva, isso funciona muito bem. Vergonha sobre a duplicação de código
Adam Marshall
16

Para obter um atributo para mostrar um valor específico com base em uma verificação booleana ou ser totalmente omitido se a verificação booleana falhar, usei o seguinte:

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

Exemplo de uso:

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

Saída <div class="itWasTest">ou com <div>base no valor deparams.type

Glen
fonte
9

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

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

e quando isTrue = false: <h1>{{content.title}}</h1>

Garfi
fonte
5
e se eu quiser algo como <h1 contenteditable = "row">
SuperUberDuper
<h1 ng-attr-contenteditable="{{isTrue ? 'row' : undefined}}">{{content.title}} </h1>
PhatBuck
Essa deveria ser minha resposta aceita. Meu cenário era<video ng-attr-autoplay="{{vm.autoplay || undefined}}" />
LucasM
6

Em relação à solução aceita, a postada por Ashley Davis, o método descrito ainda imprime o atributo no DOM, independentemente do fato de que o valor que foi atribuído é indefinido.

Por exemplo, em uma configuração de campo de entrada com um atributo ng-model e value:

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

Independentemente do que está por trás do myValue, o atributo value ainda é impresso no DOM, portanto, interpretado. O modelo Ng então é substituído.

Um pouco desagradável, mas usar ng-if faz o truque:

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

Eu recomendaria usar uma verificação mais detalhada dentro das diretivas ng-if :)

alexc
fonte
Com essa abordagem, você repetirá o mesmo código.
precisa
É verdade, mas mantém a declaração do modelo segura. Meu problema ao usar a solução aceita foi que o atributo value acabou no DOM, tendo precedência sobre a declaração do modelo. Portanto, se o atributo value estiver vazio, mas o modelo não estiver, o modelo será substituído para obter um valor vazio.
alexc
6

Além disso, você pode usar uma expressão como esta:

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

Na verdade, eu escrevi um patch para fazer isso alguns meses atrás (depois que alguém perguntou sobre isso no #angularjs no freenode).

Provavelmente não será mesclado, mas é muito semelhante ao ngClass: https://github.com/angular/angular.js/pull/4269

Seja mesclado ou não, o material ng-attr- * existente provavelmente é adequado às suas necessidades (como outros já mencionaram), embora possa ser um pouco mais complicado do que a funcionalidade do estilo ngClass que você está sugerindo.

chronicsalsa
fonte
2

Para validação de campo de entrada, você pode:

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

Isto irá aplicar o atributo maxde 100somente se discountTypeé definido como%

Nestor Britez
fonte
1

Editar : Esta resposta está relacionada ao Angular2 +! Desculpe, eu perdi a etiqueta!

Resposta original:

Quanto ao caso muito simples em que você deseja aplicar (ou configurar) apenas um atributo se um determinado valor de Entrada foi definido, é tão fácil quanto

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

É o mesmo que:

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

(O valor opcional é aplicado se fornecido, caso contrário, a expressão será falsa e o atributo não será aplicado.)

Exemplo: eu tive o caso, onde permiti aplicar uma cor, mas também estilos arbitrários, em que o atributo color não funcionou como já estava definido (mesmo que a cor @Input () não tenha sido fornecida):

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

Portanto, "style.color" foi definido apenas quando o atributo color estava presente, caso contrário, o atributo color na string "styles" poderia ser usado.

Obviamente, isso também pode ser alcançado com

[style.color]="color" 

e

@Input color: (string | boolean) = false;
Zaphoid
fonte
Angular.JS é Angular 1, que é muito diferente de Angular 2+. Sua solução responde pelo Angular 2+, mas foi solicitado o AngularJS (1).
ssc-hrep3 7/06/19
@ ssc-hrep3: Você está certo. Desculpe, eu perdi isso! Obrigado. Eu editei a resposta para apontar isso. :-)
Zaphoid
é angularjs não angular, angularjs é angular 1
dgzornoza 26/03
0

No caso de você precisar de uma solução para o Angular 2, então é simples, use a vinculação de propriedades como abaixo; por exemplo, você deseja fazer com que a entrada seja lida apenas condicionalmente e, em seguida, adicione entre colchetes o atributo seguido por = sinal e expressão.

<input [readonly]="mode=='VIEW'"> 
Sanjay Singh
fonte
é angularjs (angular1) não angular (angular2)
dgzornoza 26/03
angular 2+ e Angular JS não são iguais, são mais como produtos diferentes.
Sanjay Singh
0

Conseguiu fazer isso funcionar:

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

O bom aqui é que, se item.isSelected for false, o atributo simplesmente não será renderizado.

Richard Strickland
fonte