Как привязать значения списка чекбоксов в AngularJS?
У меня есть несколько чекбоксов:
<input type='checkbox' value="apple" checked>
<input type='checkbox' value="orange">
<input type='checkbox' value="pear" checked>
<input type='checkbox' value="naartjie">
Я хотел бы привязать их к списку в моем контроллере так, чтобы при изменении состояния любого чекбокса контроллер поддерживал список всех выбранных значений, например, ['apple', 'pear']
.
Похоже, что ng-model
может привязывать только значение одного отдельного чекбокса к переменной в контроллере.
Есть ли другой способ сделать так, чтобы я мог связать четыре чекбокса со списком в контроллере?
5 ответ(ов)
Существует два подхода к решению данной проблемы. Вы можете использовать простой массив или массив объектов. У каждого из решений есть свои плюсы и минусы. Ниже приведены примеры для каждого случая.
С простым массивом в качестве входных данных
HTML может выглядеть следующим образом:
<label ng-repeat="fruitName in fruits">
<input
type="checkbox"
name="selectedFruits[]"
value="{{fruitName}}"
ng-checked="selection.indexOf(fruitName) > -1"
ng-click="toggleSelection(fruitName)"
> {{fruitName}}
</label>
Соответствующий код контроллера будет:
app.controller('SimpleArrayCtrl', ['$scope', function SimpleArrayCtrl($scope) {
// Фрукты
$scope.fruits = ['яблоко', 'апельсин', 'груша', 'наартий'];
// Выбранные фрукты
$scope.selection = ['яблоко', 'груша'];
// Переключение выбора для данного фрукта по имени
$scope.toggleSelection = function toggleSelection(fruitName) {
var idx = $scope.selection.indexOf(fruitName);
// Если фрукт выбран
if (idx > -1) {
$scope.selection.splice(idx, 1);
}
// Если фрукт не выбран
else {
$scope.selection.push(fruitName);
}
};
}]);
Плюсы: Простая структура данных, переключение по имени легко реализовать.
Минусы: Добавление/удаление может быть затруднительным, так как необходимо управлять двумя списками (входными данными и выбором).
С массивом объектов в качестве входных данных
HTML может выглядеть так:
<label ng-repeat="fruit in fruits">
<input
type="checkbox"
name="selectedFruits[]"
value="{{fruit.name}}"
ng-model="fruit.selected"
> {{fruit.name}}
</label>
А соответствующий код контроллера будет:
app.controller('ObjectArrayCtrl', ['$scope', 'filterFilter', function ObjectArrayCtrl($scope, filterFilter) {
// Фрукты
$scope.fruits = [
{ name: 'яблоко', selected: true },
{ name: 'апельсин', selected: false },
{ name: 'груша', selected: true },
{ name: 'наартий', selected: false }
];
// Выбранные фрукты
$scope.selection = [];
// Вспомогательный метод для получения выбранных фруктов
$scope.selectedFruits = function selectedFruits() {
return filterFilter($scope.fruits, { selected: true });
};
// Наблюдение за изменениями в фруктах
$scope.$watch('fruits|filter:{selected:true}', function (nv) {
$scope.selection = nv.map(function (fruit) {
return fruit.name;
});
}, true);
}]);
Плюсы: Добавление/удаление очень простое.
Минусы: Более сложная структура данных, переключение по имени может быть затруднительным или потребовать вспомогательного метода.
Вот простое решение вашей задачи:
<div ng-controller="MainCtrl">
<label ng-repeat="(color, enabled) in colors">
<input type="checkbox" ng-model="colors[color]" /> {{color}}
</label>
<p>colors: {{colors}}</p>
</div>
<script>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope){
$scope.colors = {Blue: true, Orange: true};
});
</script>
В этом коде используется AngularJS для создания контроллера с именем MainCtrl
, в котором определён объект $scope.colors
, содержащий цвета с их состоянием (включено/выключено). Используя ng-repeat
, мы динамически создаем чекбоксы для каждого цвета, а их состояние отображается в параграфе <p>
. Это позволяет легко управлять состоянием каждого чекбокса.
Вы можете протестировать решение по следующей ссылке: Plunker Demo.
Чтобы перевести приведенный код на русский в стиле ответа на StackOverflow.com, следует объяснить, что делает код, и обсуждать его элементы. Вот пример такого ответа:
В вашем коде используется AngularJS для создания чекбоксов, которые позволяют пользователю выбирать фрукты из списка. Рассмотрим, как это работает:
<input type='checkbox' ng-repeat="fruit in fruits"
ng-checked="checkedFruits.indexOf(fruit) != -1" ng-click="toggleCheck(fruit)">
ng-repeat: Этот атрибут позволяет вам итерировать по массиву
fruits
и создавать элемент<input>
для каждого фрукта в этом массиве.ng-checked: Здесь проверяется, есть ли фрукт в массиве
checkedFruits
. Если фрукт присутствует, чекбокс будет отмечен, иначе — нет.ng-click: При клике на чекбокс вызывается функция
toggleCheck(fruit)
, которая отвечает за добавление или удаление фрукта из массиваcheckedFruits
.
Теперь давайте взглянем на контроллер SomeCtrl
, который управляет логикой вашего приложения:
function SomeCtrl ($scope) {
$scope.fruits = ["apple", "orange", "pear", "naartjie"];
$scope.checkedFruits = [];
$scope.toggleCheck = function (fruit) {
if ($scope.checkedFruits.indexOf(fruit) === -1) {
$scope.checkedFruits.push(fruit);
} else {
$scope.checkedFruits.splice($scope.checkedFruits.indexOf(fruit), 1);
}
};
}
$scope.fruits: Это массив, который содержит фрукты, перечисленные в вашем интерфейсе.
$scope.checkedFruits: Этот массив хранит выбранные (отмеченные) фрукты.
toggleCheck(fruit): В этой функции проверяется, находится ли фрукт в массиве
checkedFruits
. Если нет, он добавляется. Если да, то фрукт удаляется из массива.
Обратите внимание, что в массиве fruits
вы неправильно указали фрукты (вместо отдельного массива строк, вы написали одну строку). Исправьте это на:
$scope.fruits = ["apple", "orange", "pear", "naartjie"];
Таким образом, ваш код позволит пользователю интерактивно выбирать фрукты с помощью чекбоксов, и выбранные фрукты будут отслеживаться в массиве checkedFruits
.
Вот небольшой переиспользуемый директив, который, похоже, делает то, что вам нужно. Я назвал его checkList
. Он обновляет массив при изменении состояния чекбоксов и обновляет чекбоксы, когда изменяется массив.
app.directive('checkList', function() {
return {
scope: {
list: '=checkList',
value: '@'
},
link: function(scope, elem, attrs) {
var handler = function(setup) {
var checked = elem.prop('checked');
var index = scope.list.indexOf(scope.value);
if (checked && index == -1) {
if (setup) elem.prop('checked', false);
else scope.list.push(scope.value);
} else if (!checked && index != -1) {
if (setup) elem.prop('checked', true);
else scope.list.splice(index, 1);
}
};
var setupHandler = handler.bind(null, true);
var changeHandler = handler.bind(null, false);
elem.bind('change', function() {
scope.$apply(changeHandler);
});
scope.$watch('list', setupHandler, true);
}
};
});
Вот контроллер и представление, которые показывают, как можно использовать эту директиву.
<div ng-app="myApp" ng-controller='MainController'>
<span ng-repeat="fruit in fruits">
<input type='checkbox' value="{{fruit}}" check-list='checked_fruits'> {{fruit}}<br />
</span>
<div>Следующие фрукты выбраны: {{checked_fruits | json}}</div>
<div>Добавить фрукт в массив вручную:
<button ng-repeat="fruit in fruits" ng-click='addFruit(fruit)'>{{fruit}}</button>
</div>
</div>
app.controller('MainController', function($scope) {
$scope.fruits = ['apple', 'orange', 'pear', 'naartjie'];
$scope.checked_fruits = ['apple', 'pear'];
$scope.addFruit = function(fruit) {
if ($scope.checked_fruits.indexOf(fruit) != -1) return;
$scope.checked_fruits.push(fruit);
};
});
(Кнопки демонстрируют, что изменение массива также обновляет состояние чекбоксов.)
Наконец, вот пример работы директивы на Plunker: http://plnkr.co/edit/3YNLsyoG4PIBW6Kj7dRK?p=preview
Использование строки $index
может помочь в создании хэш-таблицы выбранных значений:
<ul>
<li ng-repeat="someItem in someArray">
<input type="checkbox" ng-model="someObject[$index.toString()]" />
</li>
</ul>
Таким образом, объект ng-model
будет обновляться с ключом, представляющим индекс.
$scope.someObject = {};
Со временем $scope.someObject
будет выглядеть примерно так:
$scope.someObject = {
0: true,
4: false,
1: true
};
Этот метод не подойдет для всех ситуаций, но его легко реализовать.
В чем разница между angular-route и angular-ui-router?
Работа с $scope.$emit и $scope.$on в AngularJS
Вставка HTML в представление из контроллера AngularJS
В чем разница между String.slice и String.substring?
Проверка соответствия строки регулярному выражению в JS