我无法使用angular指令复制最简单的javascript代码

时间:2014-03-06 23:43:17

标签: javascript angularjs angularjs-directive

我写了一个非常简单的小部件/片段。悬停时五颗星(使用font-awesome),用填充星替换空星。在鼠标输出时返回默认值。如果有一个默认值的星形,并点击更改默认值取决于点击的星(例如点击第四个星会将值更改为4,依此类推。真的很简单。我不能为生活我使用角度js复制它......

我知道如果我理解正确,我需要使用指令和转换。我有很多麻烦甚至变量没有。基于默认值的填充星和空星...

如果有人能指导我,我会很感激..这是代码。

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>

非常基本的控制器

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星......

2 个答案:

答案 0 :(得分:2)

有几种不同的方法可以处理这种功能。

我已更新您的示例以显示使用隔离范围和转换​​(对于increment()按钮)。

我们还将明星标记捆绑到ratingWidget指令中,使其成为模块化的,并将其保留为更多的独立组件。

您可以看到,由于ng-repeatng-class指令,如果我们不想,我们不必直接使用HTML元素,Angular通过数据绑定处理繁重的工作。

这是一个掠夺者: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上定义的方法。为此,我们需要转换内容以便绑定到父作用域。

这是一个显示此示例的plunker: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中的闭包 - 它允许您控制变量和表达式绑定的范围。

以上示例的粗略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

答案 1 :(得分:0)

评级系统定制级别和易于安装,我发现最好:https://rating-widget.com/get/rating/