Angular2 - Vinculação de botão de opção


120

Quero usar o botão de opção em um formulário usando Angular 2

Options : <br/>

1 : <input name="options" ng-control="options" type="radio" value="1"  [(ng-model)]="model.options" ><br/>

2 : <input name="options" ng-control="options" type="radio" value="2" [(ng-model)]="model.options" ><br/>

O valor inicial de model.options é 1

quando a página é carregada, o primeiro botão de opção não é verificado e as modificações não são vinculadas ao modelo

Qualquer ideia ?


Respostas:


108

use [value] = "1" em vez de value = "1"

<input name="options" ng-control="options" type="radio" [value]="1"  [(ngModel)]="model.options" ><br/>

<input name="options" ng-control="options" type="radio" [value]="2" [(ngModel)]="model.options" ><br/>

Editar:

Como sugerido por thllbrg "Para angular 2.1+ use em [(ngModel)]vez de [(ng-model)]"


7
Qual é o objetivo do atributo ng-control? Parece que tudo funciona sem ele.
Monsenhor

4
No Angular 4+, você deve usar em [(ngModel)]vez de [(ng-model)]. Leia novamente .
Claudio Holanda

1
Isso só funciona em adicionar novo modo. Não está funcionando no modo de edição. Não consegui encontrar o motivo. Em novo valor atribuído aberto para o modelo em funcionamento, mas não funciona quando recupero valores do servidor e é exibido na tela. Mas se eu exibir o valor do rótulo mostrando mas não funcionando, verificado.
Vinoth Kumar 4/17/17

4
No meu caso, acabei usando value="1" [(ngModel)]="model.options". Encerrando o valueem colchetes não foi trabalho
Silvestre D Ash

2
Estranho, mas no meu caso também, eu tive que usar value = "1" em vez de [value] = "1". Estou usando Angular 6
codingbbq

61

Nota - a ligação de botões de opção agora é um recurso suportado no RC4 em diante - veja esta resposta

Exemplo de botão de opção usando RadioControlValueAccessor personalizado semelhante a CheckboxControlValueAccessor ( atualizado com Angular 2 rc-1 )

App.ts

import {Component} from "@angular/core";
import {FORM_DIRECTIVES} from "@angular/common";
import {RadioControlValueAccessor} from "./radio_value_accessor";
import {bootstrap} from '@angular/platform-browser-dynamic';

@Component({
    selector: "my-app",
    templateUrl: "template.html",
    directives: [FORM_DIRECTIVES, RadioControlValueAccessor]
})
export class App {

    model;

    constructor() {
        this.model = {
            sex: "female"
        };
    }
}

template.html

<div>
    <form action="">
        <input type="radio" [(ngModel)]="model.sex"  name="sex" value="male">Male<br>
        <input type="radio" [(ngModel)]="model.sex"  name="sex" value="female">Female
    </form>

    <input type="button" value="select male" (click)="model.sex='male'">
    <input type="button" value="select female" (click)="model.sex='female'">
    <div>Selected Radio: {{model.sex}}</div>
</div>

radio_value_accessor.ts

import {Directive, Renderer, ElementRef, forwardRef} from '@angular/core';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/common';

export const RADIO_VALUE_ACCESSOR: any = {
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => RadioControlValueAccessor),
    multi: true
};

@Directive({
   selector:
       'input[type=radio][ngControl],input[type=radio][ngFormControl],input[type=radio][ngModel]',
   host: {'(change)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
   bindings: [RADIO_VALUE_ACCESSOR]
})
export class RadioControlValueAccessor implements ControlValueAccessor {
   onChange = (_) => {};
   onTouched = () => {};

   constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}

   writeValue(value: any): void {
       this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', value == this._elementRef.nativeElement.value);
   }
   registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
   registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
}

Fonte: https://github.com/angular2-school/angular2-radio-button

Demonstração ao vivo do Plunker: http://plnkr.co/edit/aggee6An1iHfwsqGoE3q?p=preview


4
Da mesma forma que as perguntas devem incluir código relevante na pergunta , as respostas também devem. Teoricamente, isso pode responder à pergunta, mas seria melhor incluir aqui as partes essenciais da resposta para usuários futuros e fornecer o link para referência. As respostas dominadas pelo link podem se tornar inválidas através da podridão do link .
Mogsdad

grande .. é estranho que não está incluído no quadro
Mourad Zouabi

Ótima solução! Uma adição pequena: Eu estou usando a entrada de css [type = "radio"]: verificado para o estilo, mas isso só funciona para mim quando usando o nativeElement de _elementRef em vez de apenas _elementRef: this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', value == this._elementRef.nativeElement.value);
bas

2
@GregWoods Atualizou a publicação com novas alterações e obrigado pela solicitação de
recebimento.

1
É agora suportado nativamente, usando RC4 angular e acima
CKLN

45

Minha solução alternativa manual, que envolve a atualização manual model.optionsquando um novo botão de opção é selecionado:

template: `
  <label *ngFor="let item of radioItems">
    <input type="radio" name="options" (click)="model.options = item" 
     [checked]="item === model.options">
    {{item}}
  </label>`

class App {
  radioItems = 'one two three'.split(' ');
  model      = { options: 'two' };
}

Isso Plunkerdemonstra o exposto acima, e também como usar um botão para alterar o botão de opção selecionado - ou seja, para provar que a ligação de dados é bidirecional:

<button (click)="model.options = 'one'">set one</button>

Eu tenho duas perguntas. primeiro: em get debug()função, o que getsignifica? a segunda é: existe alguma alternativa como essa para as caixas de seleção? forneça também um código para as caixas de seleção. obrigado +1 pela ótima resposta.
precisa saber é o seguinte

2
@PardeepJain, geté um recurso acessador do TypeScript . Poste uma pergunta para as caixas de seleção.
Mark Rajcok

pode enviar parâmetros assim '{{debug (abc)}}'?
Pardeep Jain

1
@PardeepJain, consulte plnkr.co/edit/iH3Te9EK7Y1dPXMzfWt6?p=preview . Você não pode chamar um setter como uma função, portanto Anotherdate('2015-05-18T02:30:56')não funcionará. Os setters são chamados quando você tenta atribuir um valor a uma propriedade. No meu plunker, criei uma setDate()função para \ hat aceita um novo valor de data, que depois atribui a Anotherdate. Essa atribuição chama automaticamente o setter.
Mark Rajcok

1
@PardeepJain, as {{}}ligações são reavaliadas a cada ciclo de detecção de alterações. Eu implementei ngDoCheck()no AppComponent no plunker para contar os ciclos de detecção de alterações. A partir disso, vemos que a detecção de alterações é chamada 3 vezes. No modo dev, as ligações são verificadas duas vezes , daí as 6 vezes.
Mark Rajcok

36

Aqui está a melhor maneira de usar os botões de opção no Angular2. Não há necessidade de usar o evento (clique) ou um RadioControlValueAccessor para alterar o valor da propriedade vinculada, definindo a propriedade [marcada] faz o truque.

<input name="options" type="radio" [(ngModel)]="model.options" [value]="1"
       [checked]="model.options==1" /><br/>
<input name="options" type="radio"  [(ngModel)]="model.options" [value]="2"
       [checked]="model.options==2" /><br/>

Publiquei um exemplo do uso de botões de opção: Angular 2: como criar botões de opção a partir de enum e adicionar ligação bidirecional? Funciona a partir de pelo menos Angular 2 RC5.


2
Isso só funciona em adicionar novo modo. Não está funcionando no modo de edição. Não consegui encontrar o motivo. Em novo valor atribuído aberto para o modelo em funcionamento, mas não funciona quando recupero valores do servidor e é exibido na tela. Mas se eu exibir o valor do rótulo mostrando mas não funcionando, verificado.
Vinoth Kumar

1
@VinothKumar Você conseguiu fazer o modo de edição funcionar? Estou tendo o mesmo problema
Dave Nottage

18

Este problema foi resolvido na versão Angular 2.0.0-rc.4, respectivamente em formulários.

Inclua "@angular/forms": "0.2.0"no package.json.

Em seguida, estenda seu bootstrap em main. Parte relevante:

...
import { AppComponent } from './app/app.component';
import { disableDeprecatedForms, provideForms } from '@angular/forms';

bootstrap(AppComponent, [
    disableDeprecatedForms(),
    provideForms(),
    appRouterProviders
]);

Eu tenho isso em .html e funciona perfeitamente: value: {{buildTool}}

<form action="">
    <input type="radio" [(ngModel)]="buildTool" name="buildTool" value="gradle">Gradle <br>
    <input type="radio" [(ngModel)]="buildTool" name="buildTool" value="maven">Maven
</form>

esta é a resposta correta a partir do rc4 e, apenas para adicionar, os rádios podem ser usados ​​com enumerações.
Ron

8
correndo RC7, eu precisava para colocar entre colchetes [valor]
Brian Vander Plaats

1
Eu acho que você precisa de colchetes porque você está usando uma variável do seu componente em vez de uma string, no meu caso, a resposta do @ Zolcsi funcionou bem!
Naeem Baghi

1
Esta parte com disableDeprecatedFormse provideFormsparece mágica e não faz nenhum sentido. O que essas coisas fazem? Isso é código ilegível redundante, tornando coisas imprevisíveis em escala desconhecida.
Gherman

6

Eu estava procurando o método certo para lidar com esses botões de opção. Aqui está um exemplo de uma solução que encontrei aqui:

<tr *ngFor="let entry of entries">
    <td>{{ entry.description }}</td>
    <td>
        <input type="radio" name="radiogroup" 
            [value]="entry.id" 
            (change)="onSelectionChange(entry)">
    </td>
</tr>

Observe o onSelectionChange que passa o elemento atual para o método


4

A entrada de rádio ainda não parece ser suportada. Deve haver um valor de entrada de rádio acessor (semelhante ao da caixa um , onde ele define 'checada' attr aqui ), mas não havia nenhuma. Então eu implementei um; você pode conferir aqui .



@ JimB: infelizmente, parece que a semântica do nativo é diferente .
Kiara Grouwstra

4

[value] = "item" usando * ngFor também funciona com formas reativas nos angulares 2 e 4

<label *ngFor="let item of items">
    <input type="radio" formControlName="options" [value]="item">
    {{item}}
</label>`

1
Como fazer seleção única?
Belter

4

O seguinte problema foi corrigido, considere adicionar entrada de rádio dentro da formtag e use-a [value]para exibir o valor.

<form name="form" (ngSubmit)="">
    <div *ngFor="let item of options">
        <input [(ngModel)]="model.option_id" type="radio" name="options" [value]="item.id"> &nbsp; {{ item.name }}
    </div>
</form>

3

Aqui está uma solução que funciona para mim. Envolve a ligação do botão de opção - mas não a ligação aos dados corporativos, mas a ligação ao estado do botão de opção. Provavelmente NÃO é a melhor solução para novos projetos, mas é apropriada para o meu projeto. Meu projeto tem uma tonelada de código existente escrito em uma tecnologia diferente que estou portando para o Angular. O código antigo segue um padrão no qual o código está muito interessado em examinar cada botão de opção para ver se é o selecionado. A solução é uma variação das soluções do manipulador de cliques, algumas das quais já foram mencionadas no Stack Overflow. O valor agregado desta solução pode ser:

  1. Funciona com o padrão de código antigo com o qual tenho que trabalhar.
  2. Criei uma classe auxiliar para tentar reduzir o número de instruções "if" no manipulador de cliques e manipular qualquer grupo de botões de opção.

Esta solução envolve

  1. Usando um modelo diferente para cada botão de opção.
  2. Configurando o atributo "marcado" com o modelo do botão de opção.
  3. Passando o modelo do botão de opção clicado para a classe auxiliar.
  4. A classe auxiliar garante que os modelos estejam atualizados.
  5. No "horário de envio", isso permite que o código antigo examine o estado dos botões de opção para ver qual deles é selecionado examinando os modelos.

Exemplo:

<input type="radio"
    [checked]="maleRadioButtonModel.selected"
    (click)="radioButtonGroupList.selectButton(maleRadioButtonModel)"

...

 <input type="radio"
    [checked]="femaleRadioButtonModel.selected"
    (click)="radioButtonGroupList.selectButton(femaleRadioButtonModel)"

...

Quando o usuário clica em um botão de opção, o método selectButton da classe auxiliar é chamado. É passado o modelo para o botão de opção que foi clicado. A classe auxiliar define o campo booleano "selecionado" do modelo passado em como true e define o campo "selecionado" de todos os outros modelos de botão de opção como false.

Durante a inicialização, o componente deve construir uma instância da classe auxiliar com uma lista de todos os modelos de botão de opção no grupo. No exemplo, "radioButtonGroupList" seria uma instância da classe auxiliar cujo código é:

 import {UIButtonControlModel} from "./ui-button-control.model";


 export class UIRadioButtonGroupListModel {

  private readonly buttonList : UIButtonControlModel[];
  private readonly debugName : string;


  constructor(buttonList : UIButtonControlModel[], debugName : string) {

    this.buttonList = buttonList;
    this.debugName = debugName;

    if (this.buttonList == null) {
      throw new Error("null buttonList");
    }

    if (this.buttonList.length < 2) {
      throw new Error("buttonList has less than 2 elements")
    }
  }



  public selectButton(buttonToSelect : UIButtonControlModel) : void {

    let foundButton : boolean = false;
    for(let i = 0; i < this.buttonList.length; i++) {
      let oneButton : UIButtonControlModel = this.buttonList[i];
      if (oneButton === buttonToSelect) {
        oneButton.selected = true;
        foundButton = true;
      } else {
        oneButton.selected = false;
      }

    }

    if (! foundButton) {
      throw new Error("button not found in buttonList");
    }
  }
}

2

Exemplo de listagem de rádio angular 8:

Link de origem

insira a descrição da imagem aqui

Resposta JSON

    [
            {
                "moduleId": 1,
                "moduleName": "Employee",
                "subModules":[
                    {
                        "subModuleId": 1,
                        "subModuleName": "Add Employee",
                        "selectedRightType": 1,
                    },{
                        "subModuleId": 2,
                        "subModuleName": "Update Employee",
                        "selectedRightType": 2,
                    },{
                        "subModuleId": 3,
                        "subModuleName": "Delete Employee",
                        "selectedRightType": 3,
                    }
                ]
            },  
            {
                "moduleId": 2,
                "moduleName": "Company",
                "subModules":[
                    {
                        "subModuleId": 4,
                        "subModuleName": "Add Company",
                        "selectedRightType": 1,
                    },{
                        "subModuleId": 5,
                        "subModuleName": "Update Company",
                        "selectedRightType": 2,
                    },{
                        "subModuleId": 6,
                        "subModuleName": "Delete Company",
                        "selectedRightType": 3,
                    }
                ]
            },  
            {
                "moduleId": 3,
                "moduleName": "Tasks",
                "subModules":[
                    {
                        "subModuleId": 7,
                        "subModuleName": "Add Task",
                        "selectedRightType": 1,
                    },{
                        "subModuleId": 8,
                        "subModuleName": "Update Task",
                        "selectedRightType": 2,
                    },{
                        "subModuleId": 9,
                        "subModuleName": "Delete Task",
                        "selectedRightType": 3,
                    }
                ]
            }
    ]

Modelo HTML

        <div *ngFor="let module of modules_object">
            <div>{{module.moduleName}}</div>
            <table width="100%">

                <thead>
                    <tr>
                        <th>Submodule</th>
                        <th>
                            <input type="radio" name="{{module.moduleName}}_head_radio" [(ngModel)]="module.selHeader" (change)="selAllColumn(module)" [value]="1"> Read Only
                        </th>
                        <th>
                            <input type="radio" name="{{module.moduleName}}_head_radio" [(ngModel)]="module.selHeader" (change)="selAllColumn(module)" [value]="2"> Read Write
                        </th>
                        <th>
                            <input type="radio" name="{{module.moduleName}}_head_radio" [(ngModel)]="module.selHeader" (change)="selAllColumn(module)" [value]="3"> No Access
                        </th>
                    </tr>
                </thead>

                <tbody>
                    <tr *ngFor="let sm of module.subModules">
                        <td>{{sm.subModuleName}}</td>
                        <td>
                            <input type="radio" [checked]="sm.selectedRightType == '1'" [(ngModel)]="sm.selectedRightType" name="{{sm.subModuleId}}_radio" [value]="1"> 
                        </td>
                        <td class="cl-left">
                            <input type="radio" [checked]="sm.selectedRightType == '2'" [(ngModel)]="sm.selectedRightType" name="{{sm.subModuleId}}_radio" [value]="2"> 
                        </td>
                        <td class="cl-left">
                            <input type="radio" [checked]="sm.selectedRightType == '3'" [(ngModel)]="sm.selectedRightType" name="{{sm.subModuleId}}_radio" [value]="3"> 
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>

1

Solução e solução mais simples:

<input name="toRent" type="radio" (click)="setToRentControl(false)">
<input name="toRent" type="radio" (click)="setToRentControl(true)">

setToRentControl(value){
    this.vm.toRent.updateValue(value);
    alert(value); //true/false
}

2
Como você definiria, neste caso, um botão de opção para o valor padrão desde o início?
EricC

Também não haveria situação onde o usuário muda escolha freqüência, everythime haveria uma função exectuted para cada cheque,
Blackhawk

1

Eu criei uma versão usando apenas um evento click nos elementos carregados e passando o valor da seleção para a função "getSelection" e atualizando o modelo.

No seu modelo:

<ul>
     <li *ngFor="let p of price"><input type="radio" name="price"      (click)="getValue(price.value)" value="{{p}}" #price> {{p}} 
     </li>
</ul>

Sua classe:

export class App {

  price:string;

  price = ["1000", "2000", "3000"];

  constructor() {   }

  model = new SomeData(this.price);

  getValue(price){
    this.model.price = price;
  }
}

Veja o exemplo: https://plnkr.co/edit/2Muje8yvWZVL9OXqG0pW?p=info


1

Por mais que essa resposta possa não ser a melhor, dependendo do seu caso de uso, ela funciona. Em vez de usar o botão de opção para uma seleção masculina e feminina, use as <select> </select>obras perfeitamente, tanto para salvar quanto para editar.

<select formControlName="gender" name="gender" class="">
  <option value="M">Male</option>
  <option value="F">Female</option>
</select>

O exemplo acima deve funcionar perfeitamente, para editar usando o FormGroup with patchValue. Para criar, você pode usar em [(ngModel)]vez do formControlName. Ainda funciona.

No trabalho de encanamento envolvido com o botão de opção, optei por escolher o select. Visualmente e em termos de UX, não parece ser o melhor, mas do ponto de vista de um desenvolvedor, é muito mais fácil.


1

Na mudança de botão de opção, obtenha o valor dos respectivos botões com estas linhas

<label class="radio-inline">
<input class="form-check-input" type="radio" [(ngModel)]="dog" name="cat"  checked (change)="onItemChange($event)" value="Dog" />Dog</label>
<label class="radio-inline">
<input class="form-check-input" type="radio" [(ngModel)]="cat" name="cat"   (change)="onItemChange($event)" value="Cat"  />Cat</label>

https://stackblitz.com/edit/angular-jpo2dm?embed=1&file=src/app/app.component.html


0

Aqui está um código que eu uso que funciona com o Angular 7

(Nota: No passado, às vezes eu usava informações fornecidas pela resposta de Anthony Brenelière, que eu aprecio. Mas, pelo menos para o Angular 7, esta parte:

 [checked]="model.options==2"

Eu achei desnecessário.)

Minha solução aqui tem três vantagens:

  1. Consistente com as soluções mais recomendadas. Então é bom para novos projetos.
  2. Também permite que o código do botão de opção seja semelhante ao código Flex / ActionScript. Isso é importante pessoalmente porque estou traduzindo o código Flex para Angular. Como o código Flex / ActionScript, ele permite que o código funcione em um objeto de botão de opção para marcar ou desmarcar ou descobrir se um botão de opção está marcado.
  3. Ao contrário da maioria das soluções que você verá, é muito baseada em objetos. Uma vantagem é a organização: agrupa os campos de ligação de dados de um botão de opção, como selecionado, ativado, visível e possivelmente outros.

Exemplo de HTML:

       <input type="radio" id="byAllRadioButton"
                 name="findByRadioButtonGroup"
                 [(ngModel)]="findByRadioButtonGroup.dataBindingValue"
                 [value]="byAllRadioButton.MY_DATA_BINDING_VALUE">         

      <input type="radio" id="byNameRadioButton"
                 name="findByRadioButtonGroup" 
                 [(ngModel)]="findByRadioButtonGroup.dataBindingValue"
                 [value]="byNameRadioButton.MY_DATA_BINDING_VALUE">

Exemplo TypeScript:

 findByRadioButtonGroup : UIRadioButtonGroupModel
    = new UIRadioButtonGroupModel("findByRadioButtonGroup",
                                  "byAllRadioButton_value",
                                  (groupValue : any) => this.handleCriteriaRadioButtonChange(groupValue)
                                  );

  byAllRadioButton : UIRadioButtonControlModel
    = new UIRadioButtonControlModel("byAllRadioButton",
    "byAllRadioButton_value",
    this.findByRadioButtonGroup) ;

  byNameRadioButton : UIRadioButtonControlModel
    = new UIRadioButtonControlModel("byNameRadioButton",
    "byNameRadioButton_value",
    this.findByRadioButtonGroup) ;



  private handleCriteriaRadioButtonChange = (groupValue : any) : void => {

    if ( this.byAllRadioButton.selected ) {

      // Do something

    } else if ( this.byNameRadioButton.selected ) {

      // Do something

    } else {
      throw new Error("No expected radio button selected");
    }
  };

Duas classes são usadas:

Classe do grupo de botões de opção:

export class UIRadioButtonGroupModel {


  private _dataBindingValue : any;


  constructor(private readonly debugName : string,
              private readonly initialDataBindingValue : any = null,   // Can be null or unspecified
              private readonly notifyOfChangeHandler : Function = null       // Can be null or unspecified
  ) {

    this._dataBindingValue = initialDataBindingValue;
  }


  public get dataBindingValue() : any {

    return this._dataBindingValue;
  }


  public set dataBindingValue(val : any) {

    this._dataBindingValue = val;
    if (this.notifyOfChangeHandler != null) {
      MyAngularUtils.callLater(this.notifyOfChangeHandler, this._dataBindingValue);
    }
  }



  public unselectRadioButton(valueOfOneRadioButton : any) {

    //
    // Warning: This method probably never or almost never should be needed.
    // Setting the selected radio button to unselected probably should be avoided, since
    // the result will be that no radio button will be selected.  That is
    // typically not how radio buttons work.  But we allow it here.
    // Be careful in its use.
    //

    if (valueOfOneRadioButton == this._dataBindingValue) {
      console.warn("Setting radio button group value to null");
      this.dataBindingValue = null;
    }
  }

};

Classe do botão de opção

export class UIRadioButtonControlModel {


  public enabled : boolean = true;
  public visible : boolean = true;


  constructor(public readonly debugName : string,
              public readonly MY_DATA_BINDING_VALUE : any,
              private readonly group : UIRadioButtonGroupModel,
              ) {

  }


  public get selected() : boolean {

    return (this.group.dataBindingValue == this.MY_DATA_BINDING_VALUE);
  }


  public set selected(doSelectMe : boolean) {

    if (doSelectMe) {
      this.group.dataBindingValue = this.MY_DATA_BINDING_VALUE;
    } else {
      this.group.unselectRadioButton(this.MY_DATA_BINDING_VALUE);
    }
  }

}

-1

Esta pode não ser a solução correta, mas esta também é uma opção, espero que ajude alguém.

Até agora eu estava obtendo o valor de radioButtons usando o método (clique) como a seguir:

<input type="radio" name="options" #male (click)="onChange(male.value)">Male
<input type="radio" name="options" #female (click)="onChange(female.value)">Female

e no arquivo .ts eu configurei o valor da variável predefinida para obter o valor da onChangefunção.

Mas depois de pesquisar, encontrei um bom método que ainda não tentei, mas parece que este é bom usando o [(ng-model)]link aqui para o github aqui . isso está sendo usado RadioControlValueAccessorpara o rádio e também para a caixa de seleção. aqui está o # plnkr # de trabalho para esse método aqui .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.