cómo dividir los datos ng-repeat con tres columnas usando bootstrap


122

Estoy usando ng-repeat con mi código Tengo 'n' número de cuadro de texto basado en ng-repeat. Quiero alinear el cuadro de texto con tres columnas.

este es mi código

<div class="control-group" ng-repeat="oneExt in configAddr.ext">
    {{$index+1}}. 
    <input type="text" name="macAdr{{$index+1}}" 
           id="macAddress" ng-model="oneExt.newValue" value=""/>
</div>

1
Entonces, ¿suponiendo que tiene nueve (9) que desea 1,2,3 en una fila o en una columna?
Gabriele Petrioli

Respuestas:


254

El enfoque más confiable y técnicamente correcto es transformar los datos en el controlador. Aquí hay una función y uso simple de fragmentos.

function chunk(arr, size) {
  var newArr = [];
  for (var i=0; i<arr.length; i+=size) {
    newArr.push(arr.slice(i, i+size));
  }
  return newArr;
}

$scope.chunkedData = chunk(myData, 3);

Entonces su vista se vería así:

<div class="row" ng-repeat="rows in chunkedData">
  <div class="span4" ng-repeat="item in rows">{{item}}</div>
</div>

Si tiene alguna entrada dentro de ng-repeat, probablemente querrá desunir / volver a unir las matrices a medida que los datos se modifiquen o se envíen. Así es como se vería en un $watch, para que los datos siempre estén disponibles en el formato original y combinado:

$scope.$watch('chunkedData', function(val) {
  $scope.data = [].concat.apply([], val);
}, true); // deep watch

Muchas personas prefieren lograr esto en la vista con un filtro. ¡Esto es posible, pero solo debe usarse con fines de visualización! Si agrega entradas dentro de esta vista filtrada, causará problemas que pueden resolverse, pero que no son bonitos ni confiables .

El problema con este filtro es que devuelve nuevas matrices anidadas cada vez. Angular está mirando el valor de retorno del filtro. La primera vez que se ejecuta el filtro, Angular conoce el valor, luego lo ejecuta nuevamente para asegurarse de que haya terminado de cambiar. Si ambos valores son iguales, el ciclo finaliza. De lo contrario, el filtro se disparará una y otra vez hasta que sean iguales o Angular se dé cuenta de que se está produciendo un bucle de resumen infinito y se apagará. Debido a que Angular no rastreó previamente nuevas matrices / objetos anidados, siempre ve el valor de retorno como diferente al anterior. Para corregir estos filtros "inestables", debe ajustar el filtro en una memoizefunción. lodashtiene una memoizefunción y la última versión de lodash también incluye una chunkfunción, por lo que podemos crear este filtro simplemente usandonpmmódulos y compilando el script con browserifyo webpack.

Recuerde: ¡solo visualización! ¡Filtra en el controlador si estás usando entradas!

Instalar lodash:

npm install lodash-node

Crea el filtro:

var chunk = require('lodash-node/modern/array/chunk');
var memoize = require('lodash-node/modern/function/memoize');

angular.module('myModule', [])
.filter('chunk', function() {
  return memoize(chunk);
});

Y aquí hay una muestra con este filtro:

<div ng-repeat="row in ['a','b','c','d','e','f'] | chunk:3">
  <div class="column" ng-repeat="item in row">
    {{($parent.$index*row.length)+$index+1}}. {{item}}
  </div>
</div>

Ordenar artículos verticalmente

1  4
2  5
3  6

En cuanto a las columnas verticales (lista de arriba a abajo) en lugar de horizontales (de izquierda a derecha), la implementación exacta depende de la semántica deseada. Las listas que se dividen de manera desigual se pueden distribuir de diferentes maneras. Aquí hay una manera:

<div ng-repeat="row in columns">
  <div class="column" ng-repeat="item in row">
    {{item}}
  </div>
</div>
var data = ['a','b','c','d','e','f','g'];
$scope.columns = columnize(data, 3);
function columnize(input, cols) {
  var arr = [];
  for(i = 0; i < input.length; i++) {
    var colIdx = i % cols;
    arr[colIdx] = arr[colIdx] || [];
    arr[colIdx].push(input[i]);
  }
  return arr;
}

Sin embargo, la forma más directa y sencilla de obtener columnas es usar columnas CSS :

.columns {
  columns: 3;
}
<div class="columns">
  <div ng-repeat="item in ['a','b','c','d','e','f','g']">
    {{item}}
  </div>
</div>

Gracias por tu ayuda. Pero el uso de carrusel con ui-chart en angular no puede dibujar el gráfico bt carrusel funciona bien. mi código es <carousel interval = "myInterval"> <slide ng-repeat = "hito en mileStone | split: 4" active = "slide.active"> <div class = "col-md-3" ng-repeat = " hito1 en hito "> <div style =" text-align: center; "> <div ui-chart =" data "chart-options =" ​​chartOptions "> </div> </div> </div> </ slide > </carousel> E inicialicé mi variable de alcance como datos JSON de hitos.
kuppu

1
@ m59 En este momento, esta función muestra datos en la primera fila a b csegunda fila d e f. ¿Puedo mostrar a b en la primera columna c den la segunda columna y e fen la tercera columna? Gracias

1
@ Ifch0o1 Sería correcto si así fuera [].concat.call([], val). Debes considerar lo que applyhace. vales una matriz de matrices, y queremos pasar cada una de esas matrices dentro de ella como argumentos a concat. Creo que te estás centrando en el contexto de [], que no es el punto aquí. Lo que queremos es [].concat(val[1], val[2], val[3], etc), por lo que necesitamosapply([], val)
m59

1
@bahadir Actualicé la respuesta para que sea más clara. Mira lo que sucede si haces un filtro que solo devuelve []vs [[]]y [{}]. La matriz / objeto anidado hace que Angular vea un resultado diferente en cada uno filter, y sigue repitiendo buscando que el resultado permanezca igual (estabilizar).
m59

1
Realmente estás enviando spam los comentarios :) Pruébalo por ti mismo. var x = [1,2,3]; var y = [1,2,3]; console.log(x === y);La verificación profunda significa ya sea encadenar y comparar cadenas, lo cual es arriesgado porque un objeto puede no ser encadenable, o verificar recursivamente cada propiedad individualmente. Creo que Angular podría implementar eso para los filtros, pero estoy seguro de que hay buenas razones por las que no lo hicieron. Probablemente porque pretendían principalmente filtros para la modificación simple de un conjunto de datos, no una revisión completa con cambios en la estructura.
m59

64

Esta solución es muy simple:

JSON

[{id:"1",name:"testA"},{id:"2",name:"test B"},{id:"3",name:"test C"},{id:"4",name:"test D"},{id:"5",name:"test E"}]

HTML:

<div ng-controller="MyCtrl">
  <table>
    <tr ng-repeat="item in items" ng-switch on="$index % 3">
      <td ng-switch-when="0">
        {{items[$index].id}} {{items[$index].name}}
      </td>
      <td ng-switch-when="0">
        <span ng-show="items[$index+1]">
          {{items[$index+1].id}} {{items[$index+1].name}}
        </span>
      </td>
      <td ng-switch-when="0">
        <span ng-show="items[$index+2]">    
          {{items[$index+2].id}} {{items[$index+2].name}}
        </span>
      </td>
    </tr>
  </table>
</div>

DEMO en FIDDLE

ingrese la descripción de la imagen aquí


3
¡La mejor respuesta! ¡Y haga lo que sea necesario con las directivas nativas! Congratz!
Renan Franca

1
Esta solución funciona, pero en lo que respecta a las mejores prácticas, probablemente no debería usar tablas para definir el diseño en su marcado :)
Dave Cooper

2
Como @DaveCooper menciona, aquí hay un diseño alternativo sin tablas: plnkr.co/edit/81oj8mHaNyfQpCmRBWO4?p=preview . Considere tablas para datos tabulados, pero de lo contrario, este es un mejor diseño para dispositivos móviles y tamaños de pantalla variables.
Zymotik

1
Esta es, con mucho, la mejor solución. Se necesita tiempo para encontrar todas las peculiaridades cuando se utiliza un nuevo marco, pero ng-switch es un regalo del cielo
Zoran

Si va a buscar div's en lugar de una tabla, puede colocarlos ng-switch-when="0"en un div externo una vez en lugar de los 3 elementos.
Hans Wouters

19

Una solución limpia y adaptable que no requiere manipulación de datos :

El HTML:

<div class="control-group" class="label"
    ng-repeat="oneExt in configAddr.ext"
    ng-class="{'new-row': startNewRow($index, columnBreak) }">
    {{$index+1}}. 
    <input type="text" name="macAdr{{$index+1}}" 
       id="macAddress{{$index}}" ng-model="oneExt.newValue" />
</div>

El CSS:

.label {
    float: left;
    text-align: left;
}
.new-row {
    clear: left;
}

El JavaScript:

$scope.columnBreak = 3; //Max number of colunms
$scope.startNewRow = function (index, count) {
    return ((index) % count) === 0;
};

Esta es una solución simple para representar datos en filas y columnas dinámicamente sin necesidad de manipular la matriz de datos que está tratando de mostrar. Además, si intenta cambiar el tamaño de la ventana del navegador, puede ver que la cuadrícula se adapta dinámicamente al tamaño de la pantalla / div.

(También agregué un sufijo {{$ index}} a su ID ya que ng-repeat intentará crear múltiples elementos con la misma ID si no lo hace).

Un ejemplo de trabajo similar


1
Creo que el OP quería el marcado para las filas / columnas de Twitter Bootstrap, pero esto puede ser útil para alguien. Sin embargo, debe poner el código y la explicación en su respuesta. Se eliminarán las respuestas de solo enlace.
m59

@ m59 Respuesta actualizada. Gracias por la sugerencia
Cumulo Nimbus

1
Big +1 para no requiere manipulación de datos . ¡Esto funcionó perfectamente para mis necesidades de querer separar datos en un lapso usando ng-repeat en 2 columnas para que mi modal Bootstrap no fuera tan largo! ¡Me sorprendió lo simple que fue implementar esto! <span ng-repeat="z in waiverModalLinks" ng-class="{'new-row':startNewRow($index, columnBreak)}" class="{{z.id}}"><a href="{{z.link}}{{z.title}}">{{z.title}}</a></span>Eso está dentro de 'modal-body'.
Christine268

15

La respuesta de m59 es bastante buena. Lo único que no me gusta es que usa divs para lo que potencialmente podrían ser datos para una tabla.

Entonces, junto con el filtro de m59 (responda en alguna parte arriba), aquí está cómo mostrarlo en una tabla.

<table>
    <tr class="" ng-repeat="rows in foos | chunk:2">
        <td ng-repeat="item in rows">{{item}}</td>
    </tr>
</table>

7

Lo siguiente es una forma más simple:

<table>
    <tr ng-repeat="item in lists" ng-hide="$index%2!==0">
        <td>
            <label>{{ lists[$index].name}}</label>
        </td>
        <td ng-hide="!lists[$index+1]">
            <label>{{ lists[$index+1].name}}</label>
        </td>
    </tr>
</table>

La respuesta de Cumulo Nimbus es útil para mí, pero quiero que esta cuadrícula esté envuelta por un div que pueda mostrar la barra de desplazamiento cuando la lista es demasiado larga.

Para lograr esto, agregué style="height:200px; overflow:auto"un div alrededor de la tabla que hace que se muestre como una sola columna.

Ahora funciona para una longitud de matriz de uno.


cuando solo hay 1 elemento en la matriz, no se mostrará la primera fila. para solucionar este cambio el ng-hide="$index%2!==0"
Anuj Pandey

4

Tengo una función y la almacené en un servicio para poder usarla en toda mi aplicación:

Servicio:

app.service('SplitArrayService', function () {
return {
    SplitArray: function (array, columns) {
        if (array.length <= columns) {
            return [array];
        };

        var rowsNum = Math.ceil(array.length / columns);

        var rowsArray = new Array(rowsNum);

        for (var i = 0; i < rowsNum; i++) {
            var columnsArray = new Array(columns);
            for (j = 0; j < columns; j++) {
                var index = i * columns + j;

                if (index < array.length) {
                    columnsArray[j] = array[index];
                } else {
                    break;
                }
            }
            rowsArray[i] = columnsArray;
        }
        return rowsArray;
    }
}

});

Controlador:

$scope.rows   = SplitArrayService.SplitArray($scope.images, 3); //im splitting an array of images into 3 columns

Margen:

 <div class="col-sm-12" ng-repeat="row in imageRows">
     <div class="col-sm-4" ng-repeat="image in row">
         <img class="img-responsive" ng-src="{{image.src}}">
     </div>
 </div>

Si bien esto es genial, no ayuda cuando se usan los otros filtros, como orderBy o filter.
Robbie Smith

2

Mi enfoque fue una mezcla de cosas.

Mi objetivo era tener una cuadrícula que se adaptara al tamaño de la pantalla. Quería 3 columnas para lg, 2 columnas para smy md, y 1 columna para xs.

Primero, creé la siguiente función de alcance, usando el $windowservicio angular :

$scope.findColNumberPerRow = function() {
    var nCols;
    var width = $window.innerWidth;

    if (width < 768) {
        // xs
        nCols = 1;
    }
    else if(width >= 768 && width < 1200) {
         // sm and md
         nCols = 2
    } else if (width >= 1200) {
        // lg
        nCols = 3;
    }
    return nCols;
};

Luego, utilicé la clase propuesta por @Cumulo Nimbus:

.new-row {
    clear: left;
}

En el div que contiene el ng-repeat, agregué la resizabledirectiva, como se explica en esta página , de modo que cada vez que se cambia el tamaño de la ventana, el $windowservicio angular se actualiza con los nuevos valores.

Finalmente, en el div repetido tengo:

ng-repeat="user in users" ng-class="{'new-row': ($index % findColNumberPerRow() === 0) }"

Por favor, hágame saber cualquier falla en este enfoque.

Espero que pueda ser útil.


2

Un simple truco con CSS "clearfix" recomendado por Bootstrap:

<div class="row">
      <div ng-repeat-start="value in values" class="col-md-4">
        {{value}}
      </div>
      <div ng-repeat-end ng-if="$index % 3 == 0" class="clearfix"></div>
</div>

Muchas ventajas: eficiente, rápido, utilizando las recomendaciones de Boostrap, evitando posibles problemas de $ digest y sin alterar el modelo angular.


1

Este ejemplo produce un repetidor anidado donde los datos externos incluyen una matriz interna que quería enumerar en dos columnas. El concepto sería válido para tres o más columnas.

En la columna interior repito la "fila" hasta que se alcanza el límite. El límite se determina dividiendo la longitud de la matriz de elementos por el número de columnas deseadas, en este caso por dos. El método de división se encuentra en el controlador y se le pasa la longitud de la matriz actual como parámetro. La función de división de JavaScript (0, array.length / columnCount) luego aplicó el límite al repetidor.

Luego se invoca el segundo repetidor de columna y repite el segmento (array.length / columnCount, array.length) que produce la segunda mitad del array en la columna dos.

<div class="row" ng-repeat="GroupAccess in UsersController.SelectedUser.Access" ng-class="{even: $even, odd: $odd}">
    <div class="col-md-12 col-xs-12" style=" padding-left:15px;">
        <label style="margin-bottom:2px;"><input type="checkbox" ng-model="GroupAccess.isset" />{{GroupAccess.groupname}}</label>
    </div>
    <div class="row" style=" padding-left:15px;">
        <div class="col-md-1 col-xs-0"></div>
        <div class="col-md-3 col-xs-2">
            <div style="line-height:11px; margin-left:2px; margin-bottom:2px;" ng-repeat="Feature in GroupAccess.features.slice(0, state.DivideBy2(GroupAccess.features.length))">
                <span class="GrpFeature">{{Feature.featurename}}</span>
            </div>
        </div>
        <div class="col-md-3 col-xs-2">
            <div style="line-height:11px; margin-left:2px; margin-bottom:2px;" ng-repeat="Feature in GroupAccess.features.slice( state.DivideBy2(GroupAccess.features.length), GroupAccess.features.length )">
                <span class="GrpFeature">{{Feature.featurename}}</span>
            </div>
        </div>
        <div class="col-md-5 col-xs-8">
        </div>
    </div>
</div>


// called to format two columns
state.DivideBy2 = function(nValue) {
    return Math.ceil(nValue /2);
};

Espero que esto ayude a ver la solución de otra manera. (PD: esta es mi primera publicación aquí :-))


1

Arreglo sin .row

<div class="col col-33 left" ng-repeat="photo in photos">
   Content here...
</div>

y css

.left {
  float: left;
}

1

Aquí hay una manera fácil de hacerlo. Es más manual y termina con un marcado desordenado. No recomiendo esto, pero hay situaciones en las que esto podría ser útil.

Aquí hay un enlace de violín http://jsfiddle.net/m0nk3y/9wcbpydq/

HTML:

<div ng-controller="myController">
    <div class="row">
        <div class="col-sm-4">
            <div class="well">
                <div ng-repeat="person in people = data | limitTo:Math.ceil(data.length/3)">
                {{ person.name }}
                </div>
            </div>
        </div>
        <div class="col-sm-4">
            <div class="well">
                <div ng-repeat="person in people = data | limitTo:Math.ceil(data.length/3):Math.ceil(data.length/3)">
                {{ person.name }}
                </div>
            </div>
        </div>
        <div class="col-sm-4">
            <div class="well">
                <div ng-repeat="person in people = data | limitTo:Math.ceil(data.length/3):Math.ceil(data.length/3)*2">
                {{ person.name }}
                </div>
            </div>
        </div>
    </div>
</div>

JS:

var app = angular.module('myApp', []);

app.controller('myController', function($scope) {

    $scope.Math = Math;
    $scope.data = [
        {"name":"Person A"},
        ...
    ];
});

Esta configuración requiere que usemos algunas matemáticas en el marcado: /, por lo que deberá inyectar matemáticas agregando esta línea: $scope.Math = Math;


1

Todas estas respuestas parecen excesivamente diseñadas.

De lejos, el método más simple sería configurar los divs de entrada en una columna de arranque col-md-4, luego la rutina de arranque lo formateará automáticamente en 3 columnas debido a la naturaleza de 12 columnas de la rutina de arranque:

<div class="col-md-12">
    <div class="control-group" ng-repeat="oneExt in configAddr.ext">
        <div class="col-md-4">
            <input type="text" name="macAdr{{$index}}"
                   id="macAddress" ng-model="oneExt.newValue" value="" />
        </div>
    </div>
</div>

1
<div class="row">
  <div class="col-md-4" ng-repeat="remainder in [0,1,2]">
    <ul>
      <li ng-repeat="item in items" ng-if="$index % 3 == remainder">{{item}}</li>
    </ul>
  </div>
</div>

0

Basándome en la muy buena respuesta de m59. Descubrí que las entradas del modelo se verían borrosas si cambiaran, por lo que solo podría cambiar un carácter a la vez. Este es uno nuevo para listas de objetos para cualquier persona que lo necesite:

EDITAR actualizado para manejar múltiples filtros en una página

app.filter('partition', function() {
  var cache = {}; // holds old arrays for difference repeat scopes
  var filter = function(newArr, size, scope) {
    var i,
      oldLength = 0,
      newLength = 0,
      arr = [],
      id = scope.$id,
      currentArr = cache[id];
    if (!newArr) return;

    if (currentArr) {
      for (i = 0; i < currentArr.length; i++) {
        oldLength += currentArr[i].length;
      }
    }
    if (newArr.length == oldLength) {
      return currentArr; // so we keep the old object and prevent rebuild (it blurs inputs)
    } else {
      for (i = 0; i < newArr.length; i += size) {
        arr.push(newArr.slice(i, i + size));
      }
      cache[id] = arr;
      return arr;
    }
  };
  return filter;
}); 

Y este sería el uso:

<div ng-repeat="row in items | partition:3:this">
  <span class="span4">
    {{ $index }}
  </span>
</div>

Por lo que puedo decir, esto debería funcionar la mayor parte del tiempo, pero esa lógica de almacenamiento en caché no parece completamente confiable y pasar el alcance es incómodo. Realmente recomiendo simplemente filtrar dentro del controlador cuando se necesitan entradas. Es el enfoque técnicamente correcto de todos modos.
m59

0

Soy nuevo en bootstrap y angularjs, pero esto también podría hacer Array por 4 elementos como un grupo, el resultado será como 3 columnas. Este truco utiliza el principio de la línea de ruptura bootstrap.

<div class="row">
 <div class="col-sm-4" data-ng-repeat="item in items">
  <div class="some-special-class">
   {{item.XX}}
  </div>
 </div>
</div>

0

Lodash tiene un método integrado ahora que yo personalmente uso: https://lodash.com/docs#chunk

En base a esto, el código del controlador podría tener el siguiente aspecto:

$scope.groupedUsers = _.chunk( $scope.users, 3 )

Ver código:

<div class="row" ng-repeat="rows in groupedUsers">
  <div class="span4" ng-repeat="item in rows">{{item}}</div>
</div>

0

Otra forma se establece width:33.33%; float:leften un contenedor wrap div como este:

<div ng-repeat="right in rights" style="width: 33.33%;float: left;">
    <span style="width:60px;display:inline-block;text-align:right">{{$index}}</span>
    <input type="number" style="width:50px;display:inline-block" ">
</div>

0

Me encontré en un caso similar, queriendo generar grupos de visualización de 3 columnas cada uno. Sin embargo, aunque estaba usando bootstrap, estaba tratando de separar estos grupos en diferentes divs principales. También quería hacer algo genéricamente útil.

Lo abordé con 2 de la ng-repeatsiguiente manera:

<div ng-repeat="items in quotes" ng-if="!($index % 3)">
  <div ng-repeat="quote in quotes" ng-if="$index <= $parent.$index + 2 && $index >= $parent.$index">
                ... some content ...
  </div>
</div>

Esto hace que sea muy fácil cambiar a un número diferente de columnas y separarse en varios divs principales.


0

En caso de que alguien quiera un Angular 7 (y una versión superior), aquí hay un ejemplo que utilicé en una de mis aplicaciones:

HTML:

                   <div class="container-fluid">
                    <div class="row" *ngFor="let reports of chunkData; index as i">
                        <div *ngFor="let report of reports" class="col-4 col-sm-4"
                            style="border-style:solid;background-color: antiquewhite">{{report}}</div>
                    </div>
                </div>

ARCHIVO .TS

export class ConfirmationPageComponent implements OnInit {
  chunkData = [];
  reportsArray = ["item 1", "item 2", "item 3", "item 4", "item 5", "item 6"];
  
  constructor() {}

  ngOnInit() {
    this.chunkData = this.chunk(this.reportsArray, 3);
  }

  chunk(arr, size) {
    var newArr = [];
    for (var i = 0; i < arr.length; i += size) {
      newArr.push(arr.slice(i, i + size));
    }
    return newArr;
  }
}

Esta es una solución increíble para crear dinámicamente nuevas columnas / filas según la cantidad de elementos que itera desde la base de datos. ¡Gracias!


-1

Esto responde a la pregunta original que es cómo obtener 1,2,3 en una columna. - preguntado por kuppu 8 de febrero de 14 a 13:47

código angularjs:

function GetStaffForFloor(floor) {
    var promiseGet = Directory_Service.getAllStaff(floor);
    promiseGet.then(function (pl) {
        $scope.staffList = chunk(pl.data, 3); //pl.data; //
    },
    function (errorOD) {
        $log.error('Errored while getting staff list.', errorOD);
    });
}
function chunk(array, columns) {
    var numberOfRows = Math.ceil(array.length / columns);

    //puts 1, 2, 3 into column
    var newRow = []; //array is row-based.
    for (var i = 0; i < array.length; i++) {
        var columnData = new Array(columns);
        if (i == numberOfRows) break;
        for (j = 0; j < columns; j++)
        {
            columnData[j] = array[i + numberOfRows * j];
        }
        newRow.push(columnData); 
    }
    return newRow;

    ////this works but 1, 2, 3 is in row
    //var newRow = [];
    //for (var i = 0; i < array.length; i += columns) {
    //    newRow.push(array.slice(i, i + columns)); //push effectively does the pivot. array is row-based.
    //}
    //return newRow;
};

Ver código (nota: usando bootstrap 3):

<div class="staffContainer">
    <div class="row" ng-repeat="staff in staffList">
        <div class="col-md-4" ng-repeat="item in staff">{{item.FullName.length > 0 ? item.FullName + ": Rm " + item.RoomNumber : ""}}</div>
    </div>
</div>

-1

Este código ayudará a alinear los elementos con tres columnas en modo lg y md, dos columnas en modo sm y una sola columna es modo xs

<div class="row">
<div ng-repeat="oneExt in configAddr.ext">
    <div class="col-xs-12 col-sm-6 col-md-4 col-lg-4">
        {$index+1}}. 
        <input type="text" name="macAdr{{$index+1}}" 
       id="macAddress" ng-model="oneExt.newValue" value=""/>
    </div>
</div>

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.