是否有任何Angular JS Tabs指令允许对它们进行重新排序(如浏览器的标签)
如果没有开始实施会很棒
<tabset>
<tab ng-repeat="tab in vm.tabs" active="tab.active" sortable-tab> </tab>
<tab disabled="true" ng-click"vm.addNewTab()" class="nonSortable-addTab-plusButton"></tab>
</tabset>
如何让它们重新制作?
编辑:Bounty添加使用上面的原始tabset语法。
答案 0 :(得分:14)
使用Angular UI Bootstrap tabset
,仅使用sortable-tab
指令:
<tabset>
<tab sortable-tab ng-repeat="tab in tabs" heading="{{tab.title}}" active="tab.active" disabled="tab.disabled">
<p>{{tab.content}}</p>
</tab>
<tab disabled="true">
<tab-heading>
<i class="glyphicon glyphicon-plus"></i>
</tab-heading>
</tab>
</tabset>
首先,它需要一些技巧/黑客来与ngRepeat
集成,因此它可以重新排序数组。它(重新)解析ng-repeat
属性,并从范围中获取数组,就像ngRepeat
一样
// Attempt to integrate with ngRepeat
// https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js#L211
var match = attrs.ngRepeat.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
var tabs;
scope.$watch(match[2], function(newTabs) {
tabs = newTabs;
});
然后,您还可以在示波器上观察$index
变量,以确保您总是拥有当前元素的最新索引:
var index = scope.$index;
scope.$watch('$index', function(newIndex) {
index = newIndex;
});
然后使用HTML5 drag and drop,通过setData
和getData
attrs.$set('draggable', true);
// Wrapped in $apply so Angular reacts to changes
var wrappedListeners = {
// On item being dragged
dragstart: function(e) {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.dropEffect = 'move';
e.dataTransfer.setData('application/json', index);
element.addClass('dragging');
},
dragend: function(e) {
e.stopPropagation();
element.removeClass('dragging');
},
dragleave: function(e) {
element.removeClass('hover');
},
drop: function(e) {
e.preventDefault();
e.stopPropagation();
var sourceIndex = e.dataTransfer.getData('application/json');
move(sourceIndex, index);
element.removeClass('hover');
}
};
// For performance purposes, do not
// call $apply for these
var unwrappedListeners = {
dragover: function(e) {
e.preventDefault();
element.addClass('hover');
},
/* Use .hover instead of :hover. :hover doesn't play well with
moving DOM from under mouse when hovered */
mouseenter: function() {
element.addClass('hover');
},
mouseleave: function() {
element.removeClass('hover');
}
};
angular.forEach(wrappedListeners, function(listener, event) {
element.on(event, wrap(listener));
});
angular.forEach(unwrappedListeners, function(listener, event) {
element.on(event, listener);
});
function wrap(fn) {
return function(e) {
scope.$apply(function() {
fn(e);
});
};
}
注意:对于某些悬停效果,使用hover
类而不是:hover
有一些黑客攻击。这部分是由于CSS :hover
样式在从鼠标下方重新排列后才被删除,至少在Chrome中是这样。
实际移动标签的功能,采用ngRepeat
使用的数组,然后重新排序:
function move(fromIndex, toIndex) {
// http://stackoverflow.com/a/7180095/1319998
tabs.splice(toIndex, 0, tabs.splice(fromIndex, 1)[0]);
};
您可以看到所有这些in a Plunker
答案 1 :(得分:2)
如果你不想使用Angular UI,比如说出于尺寸原因,你可以推出自己的基本版本。演示时间为http://plnkr.co/edit/WnvZETQlxurhgcm1k6Hd?p=preview。
你说你不需要标签是动态的,但它可能使它们更具可重用性。因此,在包装范围内,您可以:
$scope.tabs = [{
header: 'Tab A',
content: 'Content of Tab A'
},{
header: 'Tab B',
content: 'Content of Tab B'
}, {
header: 'Tab C',
content: 'Content of Tab C'
}];
设计HTML结构,您可以在上面的列表中重复按钮和内容
<tabs>
<tab-buttons>
<tab-button ng-repeat="tab in tabs">{{tab.header}}</tab-button>
</tab-buttons>
<tab-contents>
<tab-content ng-repeat="tab in tabs">{{tab.content}}</tab-body>
</tab-contents>
</tabs>
有很多方法可以做到这一点,但一种方法是在单个按钮指令上注册点击处理程序,然后将它们传达给父tabs
控制器。这可以使用require
属性完成,在父控制器上公开一个方法,在本例中为show
,并通过传递$index
ngRepeat
来传递按钮的当前索引。 {1}}增加了范围。
app.directive('tabs', function($timeout) {
return {
restrict: 'E',
controller: function($element, $scope) {
var self = this;
this.show = function(index) {
// Show only current tab
var contents = $element.find('tab-content');
contents.removeClass('current');
angular.element(contents[index]).addClass('current');
// Mark correct header as current
var buttons = $element.find('tab-button');
buttons.removeClass('current');
angular.element(buttons[index]).addClass('current');
};
$timeout(function() {
self.show('0');
});
}
};
});
app.directive('tabButton', function() {
return {
restrict: 'E',
require: '^tabs',
link: function(scope, element, attr, tabs) {
element.on('click', function() {
tabs.show(scope.$index);
});
}
};
});
假设您在页面中拥有正确的CSS,特别是.current
类的样式,就像http://plnkr.co/edit/WnvZETQlxurhgcm1k6Hd?p=preview一样,此时有一组可用的标签。
使用HTML5拖放API,您可以进行一些基本的拖放操作,而无需担心鼠标位置等问题。首先要做的是设计使其工作所需的属性。在这种情况下,父项的sortable
属性引用列表,sortable-item
属性包含对当前项的索引的引用。
<tabs sortable="tabs">
<tab-buttons>
<tab-button ng-repeat="tab in list" sortable-item="$index">{{tab.header}}</tab-button>
</tab-buttons>
<tab-contents>
<tab-content ng-repeat="tab in list">{{tab.content}}</tab-body>
</tab-contents>
</tabs>
sortable
和sortableItem
指令可以如下所示(更多详情可在http://www.html5rocks.com/en/tutorials/dnd/basics/找到)
app.directive('sortable', function() {
return {
controller: function($scope, $attrs) {
var listModel = null;
$scope.$watch($attrs.sortable, function(sortable) {
listModel = sortable;
});
this.move = function(fromIndex, toIndex) {
// http://stackoverflow.com/a/7180095/1319998
listModel.splice(toIndex, 0, listModel.splice(fromIndex, 1)[0]);
};
}
};
});
app.directive('sortableItem', function($window) {
return {
require: '^sortable',
link: function(scope, element, attrs, sortableController) {
var index = null;
scope.$watch(attrs.sortableItem, function(newIndex) {
index = newIndex;
});
attrs.$set('draggable', true);
// Wrapped in $apply so Angular reacts to changes
var wrappedListeners = {
// On item being dragged
dragstart: function(e) {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.dropEffect = 'move';
e.dataTransfer.setData('application/json', index);
element.addClass('dragging');
},
dragend: function(e) {
e.stopPropagation();
element.removeClass('dragging');
},
// On item being dragged over / dropped onto
dragenter: function(e) {
element.addClass('hover');
},
dragleave: function(e) {
element.removeClass('hover');
},
drop: function(e) {
e.preventDefault();
e.stopPropagation();
element.removeClass('hover');
var sourceIndex = e.dataTransfer.getData('application/json');
sortableController.move(sourceIndex, index);
}
};
// For performance purposes, do not
// call $apply for these
var unwrappedListeners = {
dragover: function(e) {
e.preventDefault();
}
};
angular.forEach(wrappedListeners, function(listener, event) {
element.on(event, wrap(listener));
});
angular.forEach(unwrappedListeners, function(listener, event) {
element.on(event, listener);
});
function wrap(fn) {
return function(e) {
scope.$apply(function() {
fn(e);
});
};
}
}
};
});
要点我们注意每个sortableItem
只需要了解其当前索引。如果它检测到另一个项目已被删除,则它会调用sortable
控制器上的一个函数,然后该控制器会重新排序外部作用域上的数组。 ngRepeat
然后执行常规操作并移动标签。
虽然我怀疑有更简单的解决方案,但这个解决方案具有可排序的行为,并且标签行为完全解耦。您可以对非sortable
的元素使用tabs
,并且可以在没有可排序行为的情况下使用tabs
。
答案 2 :(得分:1)
至少有两种方法可以实现它。
第一。转到http://angular-ui.github.io/bootstrap/并下载引导选项卡。 Bootstrap UI是用Angularjs编写的,包含许多有用的模块。虽然你必须自己实现一些代码来动态添加新标签,但这应该是微不足道的。只需使用ng-click创建一个按钮/ div,它会调用一个动态添加新标签的功能。
第二。用ng-repeat自己实现它。下面只是一些伪代码,可能看起来如何。
HTML:
<div class="tabs" ng-controller="TabController">
<div class="add-tab" ng-click="add_tab()"></div>
<div ng-repeat="tab in tabs" class="tab"></div>
</div>
Controller(JS):
app.controller('TabController',['$scope', function($scope){
$scope.tabs = [1, 1]
$scope.add_tab = function(){
$scope.tabs.push(1);
}
}]);
关于可排序部分。你可以创建自己的可排序(基本上给标签一个可拖动的组件,如果你这样做,你应该把它写成一个指令),使用jQuery,或者使用一些很容易通过搜索找到的Angularjs sortable / draggable。 / p>
答案 3 :(得分:1)
我对此做了 Plunker 。 为此,我使用AngularJS与来自ui-sortable的Angular-UI Angular指令。我还使用了Bootstrap tabs来简化它。
剩下要做的就是连接所有这些。
我希望这个例子可以帮到你。