我写了一个非常简单的小部件/片段。悬停时有五颗星(使用 font-awesome),用实心星替换空星。鼠标移出时返回默认值。如果有默认值,则单击星号,然后根据单击的星号更改默认值(例如单击第 4 颗星会将值更改为 4 等等。非常简单的东西。我一生都做不到我使用 Angular js 复制它...
如果我理解正确的话,我知道我需要使用指令和嵌入来做到这一点。我什至在获取变量 no 时遇到了很多麻烦。基于默认值的填充星和空星......
如果有人能指导我,我将不胜感激..这是代码。
HTML 内容
<div class="ratingList" rating-widget rate='{{ rating }}' increment="increment()">
<span>Hate it</span>
<span class="star"><i class="fa fa-star-o fa-lg"></i></span>
<span class="star"><i class="fa fa-star-o fa-lg"></i></span>
<span class="star"><i class="fa fa-star-o fa-lg"></i></span>
<span class="star"><i class="fa fa-star-o fa-lg"></i></span>
<span class="star"><i class="fa fa-star-o fa-lg"></i></span>
<span>love it</span>
非常基本的 Controller
bmApp.controller('MainController', ['$scope', function($scope){
$scope.rating = 3;
$scope.increment = function(){
$scope.rating = $scope.rating + 1;
}
}]);
罪魁祸首指令
bmApp.directive('ratingWidget', function(){
return{
restrict: 'A',
replace:true,
transclude:true,
template: '<div><button ng-click="increment()">Click</button><div class="rating"></div></div>',
controller:['$scope', '$element', '$transclude', function($scope, $element, $transclude){
$transclude(function(clone){
var stars = clone.filter('.star');
var filledStar = $('<span class="star"><i class="fa fa-star fa-lg"></i></span>');
var container = $element.find('.rating');
angular.forEach(stars, function(val, key){
var star = $(val);
if(key<$scope.rate)
{
//console.log(key);
container.append(filledStar);
//star.replaceWith(filledStar);
//angular.element(star.children()[0]).removeClass('fa-star-o').addClass('fa-star')
}else{
//console.log(key);
container.append(star);
}
});
});
}],
scope:{
rate:'@',
increment:'&'
}
}
});
我一开始就卡住了,无法根据默认值显示填充的星星...追加结果是 3 颗星...
最佳答案
有几种不同的方法可以处理此类功能。
我已经更新了您的示例,以展示隔离范围和嵌入的使用(对于 increment()
按钮)。
我们还将星号标记捆绑到 ratingWidget
指令中,使其模块化并使其更像是一个独立组件。
您可以看到,由于 ng-repeat
和 ng-class
指令,如果我们不想,我们不必直接使用 HTML 元素, Angular 通过数据绑定(bind)处理繁重的工作。
这是一个骗子:http://plnkr.co/edit/hd5DLOpRC3R9EFy316Gl?p=preview
(如果您查看该 Plunker 的历史记录,您将看到我如何使用 jQuery 直接操作元素/类)
HTML:
<div ng-app="bmApp">
<div ng-controller="MainController">
<div rating-widget rate="rating" max-rating="maxRating">
<!--
This is the content that will be transcluded.
Transclusion means that this content will linked with
the parent scope instead of being linked into the
scope of the `ratingWidget`.
i.e. the `increment()` function is defined in `MainController`
not in the `ratingWidget`.
-->
<button ng-click="increment()">Click</button>
</div>
</div>
</div>
JavaScript:
var bmApp = angular.module('bmApp', []);
bmApp.controller('MainController', ['$scope',
function($scope) {
$scope.rating = 3;
$scope.maxRating = 6;
$scope.increment = function() {
if ($scope.rating < $scope.maxRating){
$scope.rating += 1;
}
}
}]);
bmApp.directive('ratingWidget', function() {
return {
restrict: 'A',
transclude: true,
scope: {
rate: '=',
maxRating: '='
},
link: function(scope, element, attr){
var classes = {
empty: 'fa-star-o',
full: 'fa-star'
};
scope.stars = [];
scope.$watch('maxRating', function(maxRating){
maxRating = maxRating || 5;
scope.stars.length = maxRating;
for (var i = 0, len = scope.stars.length; i < len; i++){
if (!scope.stars[i]){
scope.stars[i] = {
cssClass: classes.empty
};
}
}
updateRating(scope.rate);
});
scope.$watch('rate', function(newRating){
updateRating(newRating);
});
scope.selectRating = function(index){
// The $index is zero-index but the ratings
// start at one, so add 1.
scope.rate = index + 1;
}
function updateRating(rating){
rating = rating || 0;
for (var i = 0, len = scope.stars.length; i < len; i++){
var star = scope.stars[i];
if (i < rating){
star.cssClass = classes.full;
} else {
star.cssClass = classes.empty;
}
}
}
},
template: '<div>' +
'<div class="ratingList">' +
'<span>Hate it</span>' +
'<span class="stars">' +
'<span class="star" ng-click="selectRating($index)" ng-repeat="star in stars track by $index"><i class="fa fa-lg" ng-class="star.cssClass"></i></span>' +
'</span>' +
'<span>love it</span>' +
'</div>' +
'<div ng-transclude></div' +
'</div>'
}
})
<小时/>
编辑:
@丹唐
是的,如果您的按钮位于指令外部但位于 MainController
内部,那么它将全部按预期工作,并且您不需要嵌入。
但重点是按钮位于指令内部并调用 MainController
上定义的方法。为此,我们需要嵌入内容以便绑定(bind)到父范围。
这是一个展示此示例的插件:http://plnkr.co/edit/x9xZwve9VkwbTGKUGjZJ?p=preview
HTML:
<div ng-controller="MainCtrl">
<div>I am: {{name}}</div>
<div widget>
<!--
Without transclusion this will say 'widget', with transclusion this will say 'controller'.
Transclusion lets us control the scope to which these expressions are bound.
-->
<div>I am: {{name}}</div>
</div>
</div>
JavaScript:
testApp.controller('MainCtrl', ['$scope', function($scope){
$scope.name = 'controller';
}]);
testApp.directive('widget', function(){
return {
scope: true,
transclude: true,
link: function(scope, element, attr){
scope.name = 'widget'
},
template: '<div>' +
'<div>I am: {{name}}</div>' +
'<div ng-transclude></div>' +
'</div>'
}
});
我想说 Angular 中的 transclude
就像 JavaScript 中的闭包 - 它可以让你控制变量和表达式绑定(bind)的范围。
下面是上面示例的一个粗略的 JavaScript 模拟,以显示这两个概念之间的一些相似之处:
var name = 'controller';
var printCallback = function(){
console.log('name=' + name);
}
function Widget(printCallback){
var name = 'widget';
this.printName = function(){
console.log('name=' + name);
printCallback();
}
}
var widget = new Widget(printCallback);
widget.printName();
// Prints:
// name=widget
// name=controller
关于javascript - 我在使用 Angular 指令复制最简单的 JavaScript 代码时遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22238582/