Problema de tubo OrderBy


97

Não consigo traduzir este código de Angualr 1 para Angular 2:

ng-repeat="todo in todos | orderBy: 'completed'"

Isso é o que fiz seguindo a resposta de Thierry Templier:

Modelo de componente:

*ngFor="#todo of todos | sort"

Código do componente:

@Component({
    selector: 'my-app',
    templateUrl: "./app/todo-list.component.html",
    providers: [TodoService],
    pipes: [ TodosSortPipe ]

})

Código do tubo:

import { Pipe } from "angular2/core";
import {Todo} from './todo';

@Pipe({
  name: "sort"
})
export class TodosSortPipe {
  transform(array: Array<Todo>, args: string): Array<Todo> {
    array.sort((a: any, b: any) => {
      if (a < b) {
        return -1;
      } else if (a > b) {
        return 1;
      } else {
        return 0;
      }
    });
    return array;
  }
}

Estou tentando classificar uma matriz de Todos, ordenada pela propriedade completed. Primeiro todo.completed = falsee depois o todo.complete = true.

Não entendo muito bem o transformmétodo e como passar os argumentos nesse método e no sortmétodo.

Qual é o args: stringargumento? O que são ae de bonde vêm?


Encontrei este pacote de módulo para OrderBy em Angular5 + versões freakyjolly.com/…
Code Spy

Respostas:


78

Modifiquei a resposta de @Thierry Templier para que o cachimbo possa classificar objetos personalizados em angular 4:

import { Pipe, PipeTransform } from "@angular/core";

@Pipe({
  name: "sort"
})
export class ArraySortPipe  implements PipeTransform {
  transform(array: any, field: string): any[] {
    if (!Array.isArray(array)) {
      return;
    }
    array.sort((a: any, b: any) => {
      if (a[field] < b[field]) {
        return -1;
      } else if (a[field] > b[field]) {
        return 1;
      } else {
        return 0;
      }
    });
    return array;
  }
}

E para usar:

*ngFor="let myObj of myArr | sort:'fieldName'"

Espero que isso ajude alguém.


1
Eu tenho a mensagem: The pipe 'sort' could not be found. Posso de alguma forma injetar um tubo no meu componente como em 2 tubos angulares: [ArraySortPipe]?
Matija Župančić

Veja a resposta de @Thierry Templier sobre como injetar o pipe em seu componente de aplicativo
Sal

Você precisa incluir "ArraySortPipe" em suas declarações de hierarquia de módulos. Algo como: import {ArraySortPipe} from './../../shared/filters.pipe'; Em 'app.module.ts' e qualquer módulo abaixo dele. put: declarations: [ArraySortPipe]
Dudi

71

Consulte https://angular.io/guide/pipes#apencha-no-filterpipe-or-orderbypipe para a discussão completa. Esta citação é muito relevante. Basicamente, para aplicativos de grande escala que devem ser minimizados agressivamente, a lógica de filtragem e classificação deve ser movida para o próprio componente.

"Alguns de nós podem não se importar em minimizar isso agressivamente. Essa é a nossa escolha. Mas o produto Angular não deve impedir que outra pessoa diminua agressivamente. Portanto, a equipe Angular decidiu que tudo enviado no Angular será minimizado com segurança.

A equipe do Angular e muitos desenvolvedores experientes do Angular recomendam fortemente que você mova a lógica de filtragem e classificação para o próprio componente. O componente pode expor uma propriedade FilterHeroes ou SortHeroes e assumir o controle sobre quando e com que freqüência executar a lógica de suporte. Quaisquer recursos que você teria colocado em um canal e compartilhado no aplicativo podem ser escritos em um serviço de filtragem / classificação e injetados no componente. "


7
Como você deve mover a lógica "para o próprio componente" de uma forma que possa "assumir o controle sobre quando e com que frequência executar a lógica de suporte"? Existem bons exemplos disso a seguir?
Mzzzzzz

1
@Mzzzzzz Onde está mencionando uma propriedade como filteredHeroese sortedHeroes, acho que a ideia é que, ao inicializar o componente, você executaria alguma lógica de classificação / filtragem (talvez chamando um método de ngOnInit), em seguida, definindo essa propriedade com os resultados classificados / filtrados, e apenas execute novamente a lógica / atualize a propriedade se houver algo que acione uma necessidade (por exemplo, a interação do usuário aciona uma chamada AJAX para obter mais heróis ou o usuário clica em uma caixa de seleção para filtrar metade deles com base em alguns critérios, etc.)
jmq

41

Você poderia implementar um canal personalizado para isso que aproveite o sortmétodo de matrizes:

import { Pipe } from "angular2/core";

@Pipe({
  name: "sort"
})
export class ArraySortPipe {
  transform(array: Array<string>, args: string): Array<string> {
    array.sort((a: any, b: any) => {
      if (a < b) {
        return -1;
      } else if (a > b) {
        return 1;
      } else {
        return 0;
      }
    });
    return array;
  }
}

E use então este tubo conforme descrito abaixo. Não se esqueça de especificar o seu tubo no pipesatributo do componente:

@Component({
  (...)
  template: `
    <li *ngFor="list | sort"> (...) </li>
  `,
  pipes: [ ArraySortPipe ]
})
(...)

É um exemplo simples para matrizes com valores de string, mas você pode ter algum processamento de classificação avançado (com base em atributos de objeto no caso de matriz de objeto, com base em parâmetros de classificação, ...).

Aqui está um plunkr para isso: https://plnkr.co/edit/WbzqDDOqN1oAhvqMkQRQ?p=preview .

Espero que ajude você, Thierry


1
Obrigado por sua resposta, você pode explicar o método de classificação?

1
Na verdade, o sortmétodo é um método do Arrayobjeto JavaScript . Veja este link: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… .
Thierry Templier

Ok, entendi, ele usa o método de classificação javascript com uma função de comparação como argumento. obrigado!

1
Infelizmente, o plunker está obsoleto. Thierry?

4
a pipes: [..]declaração não é mais válida (e não é mais necessária)
phil294

9

OrderByPipe atualizado: corrigido, sem classificação de strings.

crie uma classe OrderByPipe:

import { Pipe, PipeTransform } from "@angular/core";
@Pipe( {
name: 'orderBy'
} )
export class OrderByPipe implements PipeTransform {
transform( array: Array<any>, orderField: string, orderType: boolean ): Array<string> {
    array.sort( ( a: any, b: any ) => {
        let ae = a[ orderField ];
        let be = b[ orderField ];
        if ( ae == undefined && be == undefined ) return 0;
        if ( ae == undefined && be != undefined ) return orderType ? 1 : -1;
        if ( ae != undefined && be == undefined ) return orderType ? -1 : 1;
        if ( ae == be ) return 0;
        return orderType ? (ae.toString().toLowerCase() > be.toString().toLowerCase() ? -1 : 1) : (be.toString().toLowerCase() > ae.toString().toLowerCase() ? -1 : 1);
    } );
    return array;
  }
}

em seu controlador:

@Component({
pipes: [OrderByPipe]
})

ou em seu

 declarations: [OrderByPipe]

em seu html:

<tr *ngFor="let obj of objects | orderBy : ObjFieldName: OrderByType">

ObjFieldName: nome do campo do objeto que você deseja classificar;

OrderByType: boolean; verdadeiro: ordem decrescente; falso: ascendente;


Para argumentos de string comparando a [orderField] - b [orderField] retorna NaN
Piotr Pęczek

Para argumentos de data, não funciona. O formato da data como texto será ordenado incorretamente.
Rafael Pizao

9

Angular não vem com um filtro orderBy pronto para uso, mas se decidirmos que precisamos de um, podemos facilmente fazer um. No entanto, existem algumas advertências que precisamos estar cientes relacionadas à velocidade e à minimização. Ver abaixo.

Um tubo simples seria algo assim.

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'sort'
})
export class SortPipe implements PipeTransform {
  transform(ary: any, fn: Function = (a,b) => a > b ? 1 : -1): any {
    return ary.sort(fn)
  }
}

Esse tubo aceita uma função de classificação ( fn) e fornece a ela um valor padrão que classificará uma matriz de primitivas de uma maneira sensata. Temos a opção de substituir esta função de classificação, se desejarmos.

Ele não aceita um nome de atributo como string, porque os nomes de atributo estão sujeitos a minificação. Eles mudarão quando reduzirmos nosso código, mas os minificadores não são inteligentes o suficiente para também reduzir o valor na string do modelo.

Primitivas de classificação (números e strings)

Podemos usar isso para classificar uma matriz de números ou strings usando o comparador padrão:

import { Component } from '@angular/core';

@Component({
  selector: 'cat',
  template: `
    {{numbers | sort}}
    {{strings | sort}}
  `
})
export class CatComponent
  numbers:Array<number> = [1,7,5,6]
  stringsArray<string> = ['cats', 'hats', 'caveats']
}

Classificando uma série de objetos

Se quisermos classificar um array de objetos, podemos atribuir a ele uma função de comparador.

import { Component } from '@angular/core';

@Component({
  selector: 'cat',
  template: `
    {{cats | sort:byName}}
  `
})
export class CatComponent
  cats:Array<Cat> = [
    {name: "Missy"},
    {name: "Squoodles"},
    {name: "Madame Pompadomme"}
  ]
  byName(a,b) {
    return a.name > b.name ? 1 : -1
  }
}

Advertências - tubos puros vs. tubos impuros

Angular 2 tem um conceito de tubos puros e impuros.

Um tubo puro otimiza a detecção de alterações usando a identidade do objeto. Isso significa que o pipe só será executado se o objeto de entrada mudar de identidade, por exemplo, se adicionarmos um novo item ao array. Não vai descer aos objetos. Isso significa que, se alterarmos um atributo aninhado: this.cats[2].name = "Fluffy"por exemplo, o canal não será executado novamente. Isso ajuda o Angular a ser rápido. Os tubos angulares são puros por padrão.

Um tubo impuro, por outro lado, verificará os atributos do objeto. Isso potencialmente o torna muito mais lento. Como não pode garantir o que a função de pipe fará (talvez seja diferente com base na hora do dia, por exemplo), um pipe impuro será executado sempre que ocorrer um evento assíncrono. Isso tornará seu aplicativo consideravelmente mais lento se a matriz for grande.

O tubo acima é puro. Isso significa que ele só será executado quando os objetos na matriz forem imutáveis. Se você trocar um gato, deverá substituir todo o objeto gato por um novo.

this.cats[2] = {name:"Tomy"}

Podemos mudar o que está acima para um cano impuro definindo o atributo puro:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'sort',
  pure: false
})
export class SortPipe implements PipeTransform {
  transform(ary: any, fn: Function = (a,b) => a > b ? 1 : -1): any {
    return ary.sort(fn)
  }
}

Este tubo descerá até os objetos, mas será mais lento. Use com cuidado.


Obrigado .. Ajudou muito. Mas uma pergunta ... Se não devemos usar tubo ou filtro para classificar, qual é a melhor abordagem? Procurei em todos os lugares, todo mundo está dando solução criando cano.
Pavan Shukla de

@PavanShukla Você pode usar um pipe, apenas certifique-se de que as entradas do array sejam imutáveis ​​e crie um pipe puro. Ou, se você não tiver uma grande matriz, faça um tubo impuro e classifique cada renderização. Como alternativa, crie uma matriz classificada como um atributo de seu componente e renderize-a.
superluminário de

Eu usei a lógica array.sort no clique de cada título do cloumn. Estou fazendo esta operação na matriz de dados de exibição .. é um bom caminho?
Pavan Shukla de

7

Eu criei um pipe OrderBy que faz exatamente o que você precisa. Ele suporta a capacidade de classificar em várias colunas de uma lista de objetos também.

<li *ngFor="#todo in todos | orderBy : ['completed']">{{todo.name}} {{todo.completed}}</li>

Este tubo permite adicionar mais itens à matriz após renderizar a página e classificará a matriz com as atualizações dinamicamente.

Eu tenho um artigo sobre o processo aqui .

E aqui está uma demonstração operacional: http://fuelinteractive.github.io/fuel-ui/#/pipe/orderby e https://plnkr.co/edit/DHLVc0?p=info


Você não está lidando com valores nulos.
Ali Habibzadeh

if (a == null) a = 0; if (b == null) b = 0;
Ali Habibzadeh

Além disso, valores com valor igual são saltados e se movem conforme você clica na interface
Ali Habibzadeh

@XGreen obrigado por isso. Estarei adicionando suporte para valores nulos / indefinidos na próxima atualização do fuel-ui. Quanto ao nervosismo de valores iguais, não estou vendo isso. Qual navegador você está usando?
Cory Shaw

Versão do Chrome 50.0.2661.86 (64 bits), OSX El Capitan
Ali Habibzadeh

4

Recomendo que você use lodash com angular, então seu cachimbo será o próximo:

import {Pipe, PipeTransform} from '@angular/core';
import * as _ from 'lodash'
@Pipe({
    name: 'orderBy'
})
export class OrderByPipe implements PipeTransform {

    transform(array: Array<any>, args?: any): any {
        return _.sortBy(array, [args]);
    }

}

e usá-lo em html como

*ngFor = "#todo of todos | orderBy:'completed'"

e não se esqueça de adicionar Pipe ao seu módulo

@NgModule({
    ...,
    declarations: [OrderByPipe, ...],
    ...
})

Gosto da sua abordagem Александр Петрик mas prefiro enviar o array no modelo: orderBy: ['field1', 'field2'] E depois chame o pipe: return _.sortBy (array, args);
Eric,

1
O problema de usar _.sortBy é que você não pode especificar a ordem descendente. Descobri que usando _.orderBy você pode especificar a ordem personalizada para cada campo. ou seja: _.orderBy (array, ['field1', 'field2'], ['asc', 'desc'])
Eric

3

Isso funcionará para qualquer campo que você passar para ele. ( IMPORTANTE: Só vai ordenar alfabeticamente, então se você passar uma data vai ordenar como alfabeto e não como data)

/*
 *      Example use
 *      Basic Array of single type: *ngFor="let todo of todoService.todos | orderBy : '-'"
 *      Multidimensional Array Sort on single column: *ngFor="let todo of todoService.todos | orderBy : ['-status']"
 *      Multidimensional Array Sort on multiple columns: *ngFor="let todo of todoService.todos | orderBy : ['status', '-title']"
 */

import {Pipe, PipeTransform} from "@angular/core";

@Pipe({name: "orderBy", pure: false})
export class OrderByPipe implements PipeTransform {

    value: string[] = [];

    static _orderByComparator(a: any, b: any): number {

        if (a === null || typeof a === "undefined") { a = 0; }
        if (b === null || typeof b === "undefined") { b = 0; }

        if (
            (isNaN(parseFloat(a)) ||
            !isFinite(a)) ||
            (isNaN(parseFloat(b)) || !isFinite(b))
        ) {
            // Isn"t a number so lowercase the string to properly compare
            a = a.toString();
            b = b.toString();
            if (a.toLowerCase() < b.toLowerCase()) { return -1; }
            if (a.toLowerCase() > b.toLowerCase()) { return 1; }
        } else {
            // Parse strings as numbers to compare properly
            if (parseFloat(a) < parseFloat(b)) { return -1; }
            if (parseFloat(a) > parseFloat(b)) { return 1; }
        }

        return 0; // equal each other
    }

    public transform(input: any, config = "+"): any {
        if (!input) { return input; }

        // make a copy of the input"s reference
        this.value = [...input];
        let value = this.value;
        if (!Array.isArray(value)) { return value; }

        if (!Array.isArray(config) || (Array.isArray(config) && config.length === 1)) {
            let propertyToCheck: string = !Array.isArray(config) ? config : config[0];
            let desc = propertyToCheck.substr(0, 1) === "-";

            // Basic array
            if (!propertyToCheck || propertyToCheck === "-" || propertyToCheck === "+") {
                return !desc ? value.sort() : value.sort().reverse();
            } else {
                let property: string = propertyToCheck.substr(0, 1) === "+" || propertyToCheck.substr(0, 1) === "-"
                    ? propertyToCheck.substr(1)
                    : propertyToCheck;

                return value.sort(function(a: any, b: any) {
                    let aValue = a[property];
                    let bValue = b[property];

                    let propertySplit = property.split(".");

                    if (typeof aValue === "undefined" && typeof bValue === "undefined" && propertySplit.length > 1) {
                        aValue = a;
                        bValue = b;
                        for (let j = 0; j < propertySplit.length; j++) {
                            aValue = aValue[propertySplit[j]];
                            bValue = bValue[propertySplit[j]];
                        }
                    }

                    return !desc
                        ? OrderByPipe._orderByComparator(aValue, bValue)
                        : -OrderByPipe._orderByComparator(aValue, bValue);
                });
            }
        } else {
            // Loop over property of the array in order and sort
            return value.sort(function(a: any, b: any) {
                for (let i = 0; i < config.length; i++) {
                    let desc = config[i].substr(0, 1) === "-";
                    let property = config[i].substr(0, 1) === "+" || config[i].substr(0, 1) === "-"
                        ? config[i].substr(1)
                        : config[i];

                    let aValue = a[property];
                    let bValue = b[property];

                    let propertySplit = property.split(".");

                    if (typeof aValue === "undefined" && typeof bValue === "undefined" && propertySplit.length > 1) {
                        aValue = a;
                        bValue = b;
                        for (let j = 0; j < propertySplit.length; j++) {
                            aValue = aValue[propertySplit[j]];
                            bValue = bValue[propertySplit[j]];
                        }
                    }

                    let comparison = !desc
                        ? OrderByPipe._orderByComparator(aValue, bValue)
                        : -OrderByPipe._orderByComparator(aValue, bValue);

                    // Don"t return 0 yet in case of needing to sort by next property
                    if (comparison !== 0) { return comparison; }
                }

                return 0; // equal each other
            });
        }
    }
}

Você pode postar um exemplo de uso?
TheUnreal de

Não consigo compilar o código que você forneceu. Recebo um erro dizendo que @Componentnão tem pipespropriedade.
Azimute

3

Isso é bom substituto para AngularJS orderby tubulação em angular 4 . Fácil e simples de usar.

Este é o URL do github para obter mais informações https://github.com/VadimDez/ngx-order-pipe

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'orderBy'
})
export class OrderPipe implements PipeTransform {

  transform(value: any | any[], expression?: any, reverse?: boolean): any {
    if (!value) {
      return value;
    }

    const isArray = value instanceof Array;

    if (isArray) {
      return this.sortArray(value, expression, reverse);
    }

    if (typeof value === 'object') {
      return this.transformObject(value, expression, reverse);
    }

    return value;
  }

  /**
   * Sort array
   *
   * @param value
   * @param expression
   * @param reverse
   * @returns {any[]}
   */
  private sortArray(value: any[], expression?: any, reverse?: boolean): any[] {
    const isDeepLink = expression && expression.indexOf('.') !== -1;

    if (isDeepLink) {
      expression = OrderPipe.parseExpression(expression);
    }

    let array: any[] = value.sort((a: any, b: any): number => {
      if (!expression) {
        return a > b ? 1 : -1;
      }

      if (!isDeepLink) {
        return a[expression] > b[expression] ? 1 : -1;
      }

      return OrderPipe.getValue(a, expression) > OrderPipe.getValue(b, expression) ? 1 : -1;
    });

    if (reverse) {
      return array.reverse();
    }

    return array;
  }


  /**
   * Transform Object
   *
   * @param value
   * @param expression
   * @param reverse
   * @returns {any[]}
   */
  private transformObject(value: any | any[], expression?: any, reverse?: boolean): any {
    let parsedExpression = OrderPipe.parseExpression(expression);
    let lastPredicate = parsedExpression.pop();
    let oldValue = OrderPipe.getValue(value, parsedExpression);

    if (!(oldValue instanceof Array)) {
      parsedExpression.push(lastPredicate);
      lastPredicate = null;
      oldValue = OrderPipe.getValue(value, parsedExpression);
    }

    if (!oldValue) {
      return value;
    }

    const newValue = this.transform(oldValue, lastPredicate, reverse);
    OrderPipe.setValue(value, newValue, parsedExpression);
    return value;
  }

  /**
   * Parse expression, split into items
   * @param expression
   * @returns {string[]}
   */
  private static parseExpression(expression: string): string[] {
    expression = expression.replace(/\[(\w+)\]/g, '.$1');
    expression = expression.replace(/^\./, '');
    return expression.split('.');
  }

  /**
   * Get value by expression
   *
   * @param object
   * @param expression
   * @returns {any}
   */
  private static getValue(object: any, expression: string[]) {
    for (let i = 0, n = expression.length; i < n; ++i) {
      const k = expression[i];
      if (!(k in object)) {
        return;
      }
      object = object[k];
    }

    return object;
  }

  /**
   * Set value by expression
   *
   * @param object
   * @param value
   * @param expression
   */
  private static setValue(object: any, value: any, expression: string[]) {
    let i;
    for (i = 0; i < expression.length - 1; i++) {
      object = object[expression[i]];
    }

    object[expression[i]] = value;
  }
}

2

Como sabemos que o filtro e a ordem são removidos do ANGULAR 2 e precisamos escrever o nosso próprio, aqui está um bom exemplo sobre plunker e artigo detalhado

Ele usou tanto o filtro quanto o orderby, aqui está o código para o pedido

import { Pipe, PipeTransform } from '@angular/core';    
@Pipe({  name: 'orderBy' })
export class OrderrByPipe implements PipeTransform {

  transform(records: Array<any>, args?: any): any {       
    return records.sort(function(a, b){
          if(a[args.property] < b[args.property]){
            return -1 * args.direction;
          }
          else if( a[args.property] > b[args.property]){
            return 1 * args.direction;
          }
          else{
            return 0;
          }
        });
    };
 }

2

Você pode usar isso para objetos:

@Pipe({
  name: 'sort',
})
export class SortPipe implements PipeTransform {

  transform(array: any[], field: string): any[] {
    return array.sort((a, b) => a[field].toLowerCase() !== b[field].toLowerCase() ? a[field].toLowerCase() < b[field].toLowerCase() ? -1 : 1 : 0);
  }

}

2

Em package.json, adicione algo como (Esta versão é adequada para Angular 2):

  "ngx-order-pipe": "^1.1.3",

Em seu módulo de texto digitado (e matriz de importações):

  import { OrderModule } from 'ngx-order-pipe';

1
<!-- const cars=['Audi','Merc','BMW','Volvo','Tesla'] -->

<ul>
  <li *ngFor="let car of cars">{{car}}</li>
</ul>


/*
 *ngFor="let c of oneDimArray | sortBy:'asc'"
 *ngFor="let c of arrayOfObjects | sortBy:'asc':'propertyName'"
*/
import { Pipe, PipeTransform } from '@angular/core';
import { orderBy } from 'lodash';

@Pipe({ name: 'sortBy' })
export class SortByPipe implements PipeTransform {

  transform(value: any[], order = '', column: string = ''): any[] {
    if (!value || order === '' || !order) { return value; } // no array
    if (!column || column === '') { return sortBy(value); } // sort 1d array
    if (value.length <= 1) { return value; } // array with only one item
    return orderBy(value, [column], [order]);
  }
}

1
Obrigado, ótima resposta
AM - EVS

0

Na versão atual do Angular2, os canais orderBy e ArraySort não são compatíveis. Você precisa escrever / usar alguns canais personalizados para isso.


0

Para a versão Angular 5+ , podemos usar o pacote ngx-order-pipe

Link do tutorial da fonte

Pacote de instalação

$ npm install ngx-order-pipe --save

Importar no módulo de aplicativos

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { OrderModule } from 'ngx-order-pipe';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    OrderModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

use em qualquer lugar

  <ul>
    <li *ngFor="let item of (dummyData | orderBy:'name') ">
      {{item.name}}
    </li>
  </ul>


-1
Component template:
todos| sort: ‘property’:’asc|desc’

Pipe code:

import { Pipe,PipeTransform  } from "angular/core";
import {Todo} from './todo';

@Pipe({
  name: "sort"
})
export class TodosSortPipe implements PipeTransform {
  transform(array: Array<Todo>, args: string): Array<Todo> {
    array.sort((a: any, b: any) => {
      if (a < b) {
        return -1;
      } else if (a > b) {
        return 1;
      } else {`enter code here`
        return 0;
      }
    });
    return array;
  }
}
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.