Управлять открытыми/сбойными событиями Аккордеона в Angular
Если у меня есть этот код:
<accordion-group heading="{{group.title}}" ng-repeat="group in groups">
{{group.content}}
</accordion-group>
Используя AngularJS, angular -ui и Twitter Bootstrap, можно ли аккордеон вызвать какое-либо действие при открытии? Я знаю, что я не могу просто добавить ng-click
, потому что он уже используется после того, как он "скомпилирован" в HTML для открытия/свертывания группы.
Ответы
Ответ 1
В группе аккордеона есть атрибут is-open
, который указывает на связываемое выражение. Вы можете посмотреть это выражение и выполнить некоторую логику, когда данная группа аккордеона открыта. Используя эту технику, вы измените свою разметку на:
<accordion-group ng-repeat="group in groups" heading="{{group.title}}" is-open="group.open">
{{group.content}}
</accordion-group>
чтобы вы могли в контроллере подготовить желаемое выражение часов:
$scope.$watch('groups[0].open', function(isOpen){
if (isOpen) {
console.log('First group was opened');
}
});
В то время как вышеприведенные работы могут быть немного громоздкими для использования на практике, поэтому, если вам кажется, что это может быть улучшено, откройте проблему в https://github.com/angular-ui/bootstrap
Ответ 2
Аккордеонные группы также допускают директиву заголовка аккордеона вместо того, чтобы предоставлять его как атрибут. Вы можете использовать это, а затем обернуть свой заголовок в другой тег с помощью ng-click.
<accordion-group ng-repeat="group in groups" heading="{{group.title}}" is-open="group.open">
<accordion-heading>
<span ng-click="opened(group, $index)">{{group.content}}</span>
</accordion-heading>
</accordion-group>
Пример: http://plnkr.co/edit/B3LC1X?p=preview
Ответ 3
Здесь решение, основанное на pkozlowski.opensource solution.
Вместо добавления $watch для каждого элемента коллекции вы можете использовать динамическое свойство . Здесь вы можете привязать свойство IsOpened группы к атрибуту is-open.
<accordion-group ng-repeat="group in groups" heading="{{group.title}}" is-open="group.IsOpened">
{{group.content}}
</accordion-group>
Итак, вы можете динамически добавлять свойство IsOpened для каждого элемента коллекции в контроллере:
$scope.groups.forEach(function(item) {
var isOpened = false;
Object.defineProperty(item, "IsOpened", {
get: function() {
return isOpened;
},
set: function(newValue) {
isOpened = newValue;
if (isOpened) {
console.log(item); // do something...
}
}
});
});
Использование свойств вместо часов лучше для выступлений.
Ответ 4
Я использовал ассоциативный массив для создания отношения между открытым состоянием и объектом модели.
HTML:
<div ng-controller="CaseController as controller">
<accordion close-others="controller.model.closeOthers">
<accordion-group ng-repeat="topic in controller.model.topics track by topic.id" is-open="controller.model.opened[topic.id]">
<accordion-heading>
<h4 class="panel-title clearfix" ng-click="controller.expand(topic)">
<span class="pull-left">{{topic.title}}</span>
<span class="pull-right">Updated: {{topic.updatedDate}}</span>
</h4>
</accordion-heading>
<div class="panel-body">
<div class="btn-group margin-top-10">
<button type="button" class="btn btn-default" ng-click="controller.createComment(topic)">Add Comment<i class="fa fa-plus"></i></button>
</div>
<div class="btn-group margin-top-10">
<button type="button" class="btn btn-default" ng-click="controller.editTopic(topic)">Edit Topic<i class="fa fa-pencil-square-o"></i></button>
</div>
<h4>Topic Description</h4>
<p><strong>{{topic.description}}</strong></p>
<ul class="list-group">
<li class="list-group-item" ng-repeat="comment in topic.comments track by comment.id">
<h5>Comment by: {{comment.author}}<span class="pull-right">Updated: <span class="commentDate">{{comment.updatedDate}}</span> | <span class="commentTime">{{comment.updatedTime}}</span></span></h5>
<p>{{comment.comment}}</p>
<div class="btn-group">
<button type="button" class="btn btn-default btn-xs" ng-click="controller.editComment(topic, comment)">Edit <i class="fa fa-pencil-square-o"></i></button>
<button type="button" class="btn btn-default btn-xs" ng-click="controller.deleteComment(comment)">Delete <i class="fa fa-trash-o"></i></button>
</div>
</li>
</ul>
</div>
</accordion-group>
</accordion>
Ключ контроллера:
self.model = {
closeOthers : false,
opened : new Array(),
topics : undefined
};
"Темы" заполняются вызовом AJAX. Разделение "открытого" состояния от объектов модели, которые обновляются с сервера, означает, что состояние сохраняется во всех обновлениях.
Я также объявляю контроллер с ng-controller="CaseController as controller"
Ответ 5
гармошка-controller.js
MyApp.Controllers
.controller('AccordionCtrl', ['$scope', function ($scope) {
$scope.groups = [
{
title: "Dynamic Group Header - 1",
content: "Dynamic Group Body - 1",
open: false
},
{
title: "Dynamic Group Header - 2",
content: "Dynamic Group Body - 2",
open: false
},
{
title: "Dynamic Group Header - 3",
content: "Dynamic Group Body - 3",
open: false
}
];
/**
* Open panel method
* @param idx {Number} - Array index
*/
$scope.openPanel = function (idx) {
if (!$scope.groups[idx].open) {
console.log("Opened group with idx: " + idx);
$scope.groups[idx].open = true;
}
};
/**
* Close panel method
* @param idx {Number} - Array index
*/
$scope.closePanel = function (idx) {
if ($scope.groups[idx].open) {
console.log("Closed group with idx: " + idx);
$scope.groups[idx].open = false;
}
};
}]);
index.html
<div ng-controller="AccordionCtrl">
<accordion>
<accordion-group ng-repeat="group in groups" is-open="group.open">
<button ng-click="closePanel($index)">Close me</button>
{{group.content}}
</accordion-group>
<button ng-click="openPanel(0)">Set 1</button>
<button ng-click="openPanel(1)">Set 2</button>
<button ng-click="openPanel(2)">Set 3</button>
</accordion>
</div>
Ответ 6
Здесь представлено решение, основанное на ответе kjv, которое легко отслеживает, какой элемент аккордеона открыт. Мне было трудно получить ng-click
для работы над заголовком аккордеона, хотя он окружает элемент в теге <span>
и добавляет ng-click, чтобы он работал нормально.
Другая проблема, с которой я столкнулся, заключалась в том, что хотя элементы accordion
были добавлены на страницу программно, контент не был. Когда я попытался загрузить содержимое с помощью директив Angular (т.е. {{path}}
), связанных с переменной $scope
, я бы нажал undefined
, следовательно, использование следующего метода, который заполняет контент аккордеона, используя идентификатор div
внедрен внутри.
Контроллер:
//initialise the open state to false
$scope.routeDescriptors[index].openState == false
function opened(index)
{
//we need to track what state the accordion is in
if ($scope.routeDescriptors[index].openState == true){ //close an accordion
$scope.routeDescriptors[index].openState == false
} else { //open an accordion
//if the user clicks on another accordion element
//then the open element will be closed, so this will handle it
if (typeof $scope.previousAccordionIndex !== 'undefined') {
$scope.routeDescriptors[$scope.previousAccordionIndex].openState = false;
}
$scope.previousAccordionIndex = index;
$scope.routeDescriptors[index].openState = true;
}
function populateDiv(id)
{
for (var x = 0; x < $scope.routeDescriptors.length; x++)
{
$("#_x" + x).html($scope.routeDescriptors[x]);
}
}
HTML:
<div ng-hide="hideDescriptions" class="ng-hide" id="accordionrouteinfo" ng-click="populateDiv()">
<accordion>
<accordion-group ng-repeat="path in routeDescriptors track by $index">
<accordion-heading>
<span ng-click="opened($index)">route {{$index}}</span>
</accordion-heading>
<!-- Notice these divs are given an ID which corresponds to it index-->
<div id="_x{{$index}}"></div>
</accordion-group>
</accordion>
</div>
Ответ 7
Вы можете сделать это с директивой Angular:
HTML
<div uib-accordion-group is-open="property.display_detail" ng-repeat="property in properties">
<div uib-accordion-heading ng-click="property.display_detail = ! property.display_detail">
some heading text
</div>
<!-- here is the accordion body -->
<div ng-init="i=$index"> <!-- I keep track of the index of ng-repeat -->
<!-- and I call a custom directive -->
<mydirective mydirective_model="properties" mydirective_index="{% verbatim ng %}{{ i }}{% endverbatim ng %}">
here is the body
</mydirective>
</div>
</div>
JS
app.directive("mydirective", function() {
return {
restrict: "EAC",
link: function(scope, element, attrs) {
/* note that ng converts everything to camelCase */
var model = attrs["mydirectiveModel"];
var index = attrs["mydirectiveIndex"];
var watched_name = model + "[" + index + "].display_detail"
scope.$watch(watched_name, function(is_displayed) {
if (is_displayed) {
alert("you opened something");
}
else {
alert("you closed something");
}
});
}
}
});
Здесь есть некоторые особенности моей настройки (я использую Django, следовательно, теги {% verbatim%} ", но метод должен работать.