【问题标题】:What is this error "[$compile:nonassign]" in angularjs directiveangularjs指令中的这个错误“[$compile:nonassign]”是什么
【发布时间】:2015-07-25 17:27:56
【问题描述】:

我正在编写指令并在控制台中获取以下内容:

错误:[$compile:nonassign] http://errors.angularjs.org/1.3.14/$compile/nonassign?p0=undefined&p1=autocomplete

事实上我想在模板中有一个动态模型。模板中的输入元素具有动态模型名称。

     <input\
        type="text"
        ng-model="modelName"\ /> 

为了获得动态模型名称,我正在创建一个标签并将路径作为模型名称传递。

  <s:bind path="${path}">
       <autocomplete url="${url}" name="${path}"  model-name="${path}"
            click-activation="true"
            on-type="doSomething" on-select="doSomethingElse">      </autocomplete>

   </s:bind>

在 $scope.watch 中,我正在查看所有型号名称。

   $scope.$watch($scope.modelName, function (newValue, oldValue) {
    } 

var app = angular.module('myApp', []);
    app.directive('autocomplete', function () {
        var array1 =[ {title:"apple", value:1},{title:"sony", value:2},{title:"LG", value:3}];
        var array2 = [{title:"java", value:1},{title:"c++", value:2},{title:"c#", value:3}];
        var index = -1;
        return {
            restrict: 'E',
            scope: {
                url: '@',
                name: '@',
                modelName:'=',
                onType: '=onType',
                onSelect: '=onSelect',
                autocompleteRequired: '='
            },
            controller: ['$scope', '$http', function ($scope, $http) {
               if($scope.url == 'url1')
                        $scope.Items = array1;
                if($scope.url == 'url2')
                        $scope.Items = array2;    

                $scope.getItems = function () {
                    return $scope.Items;
                }
                // the index of the suggestions that's currently selected
                $scope.selectedIndex = -1;

                $scope.initLock = true;

                // set new index
                $scope.setIndex = function (i) {
                    $scope.selectedIndex = parseInt(i);
                };

                this.setIndex = function (i) {
                    $scope.setIndex(i);
                    $scope.$apply();
                };

                $scope.getIndex = function (i) {
                    return $scope.selectedIndex;
                };

                // watches if the parameter filter should be changed
                var watching = true;

                // autocompleting drop down on/off
                $scope.completing = false;

                // starts autocompleting on typing in something
                $scope.$watch($scope.modelName, function (newValue, oldValue) {

                    if (oldValue === newValue || (!oldValue && $scope.initLock)) {
                        return;
                    }

                    if (watching && typeof $scope.searchParam !== 'undefined' && $scope.searchParam !== null) {
                        $scope.completing = true;
                        $scope.searchFilter = $scope.searchParam;
                        $scope.selectedIndex = -1;
                    }

                    // function thats passed to on-type attribute gets executed
                    if ($scope.onType)
                        $scope.onType($scope.searchParam);
                });

                // for hovering over suggestions
                this.preSelect = function (suggestion) {

                    watching = false;

                    // this line determines if it is shown
                    // in the input field before it's selected:
                    //$scope.searchParam = suggestion;

                    $scope.$apply();
                    watching = true;

                };

                $scope.preSelect = this.preSelect;

                this.preSelectOff = function () {
                    watching = true;
                };

                $scope.preSelectOff = this.preSelectOff;

                // selecting a suggestion with RIGHT ARROW or ENTER
                $scope.select = function (suggestion, value) {
                    if (suggestion) {
                        $scope.val = value;
                        $scope.searchParam = suggestion;
                        $scope.searchFilter = suggestion;
                        if ($scope.onSelect)
                            $scope.onSelect(suggestion);
                    }
                    watching = false;
                    $scope.completing = false;
                    setTimeout(function () {
                        watching = true;
                    }, 1000);
                    $scope.setIndex(-1);
                };


            }],
            link: function (scope, element, attrs) {

                setTimeout(function () {
                    scope.initLock = false;
                    scope.$apply();
                }, 250);

                var attr = '';

                // Default atts
                scope.attrs = {
                    "placeholder": "start typing...",
                    "class": "",
                    "id": "",
                    "inputclass": "",
                    "inputid": ""
                };

                for (var a in attrs) {
                    attr = a.replace('attr', '').toLowerCase();
                    // add attribute overriding defaults
                    // and preventing duplication
                    if (a.indexOf('attr') === 0) {
                        scope.attrs[attr] = attrs[a];
                    }
                }

                if (attrs.clickActivation) {
                    element[0].onclick = function (e) {
                        if (!scope.searchParam) {
                            setTimeout(function () {
                                scope.completing = true;
                                scope.$apply();
                            }, 200);
                        }
                    };
                }

                var key = {left: 37, up: 38, right: 39, down: 40, enter: 13, esc: 27, tab: 9};

                document.addEventListener("keydown", function (e) {
                    var keycode = e.keyCode || e.which;

                    switch (keycode) {
                        case key.esc:
                            // disable suggestions on escape
                            scope.select();
                            scope.setIndex(-1);
                            scope.$apply();
                            e.preventDefault();
                    }
                }, true);

                document.addEventListener("blur", function (e) {
                    // disable suggestions on blur
                    // we do a timeout to prevent hiding it before a click event is registered
                    setTimeout(function () {
                        scope.select();
                        scope.setIndex(-1);
                        scope.$apply();
                    }, 150);
                }, true);

                element[0].addEventListener("keydown", function (e) {
                    var keycode = e.keyCode || e.which;

                    var l = angular.element(this).find('li').length;

                    // this allows submitting forms by pressing Enter in the autocompleted field
                    if (!scope.completing || l == 0) return;

                    // implementation of the up and down movement in the list of suggestions
                    switch (keycode) {
                        case key.up:

                            index = scope.getIndex() - 1;
                            if (index < -1) {
                                index = l - 1;
                            } else if (index >= l) {
                                index = -1;
                                scope.setIndex(index);
                                scope.preSelectOff();
                                break;
                            }
                            scope.setIndex(index);

                            if (index !== -1)
                                scope.preSelect(angular.element(angular.element(this).find('li')[index]).text());

                            scope.$apply();

                            break;
                        case key.down:
                            index = scope.getIndex() + 1;
                            if (index < -1) {
                                index = l - 1;
                            } else if (index >= l) {
                                index = -1;
                                scope.setIndex(index);
                                scope.preSelectOff();
                                scope.$apply();
                                break;
                            }
                            scope.setIndex(index);

                            if (index !== -1)
                                scope.preSelect(angular.element(angular.element(this).find('li')[index]).text());

                            break;
                        case key.left:
                            break;
                        case key.right:
                        case key.enter:
                        case key.tab:

                            index = scope.getIndex();
                            // scope.preSelectOff();
                            if (index !== -1) {
                                scope.select(angular.element(angular.element(this).find('li')[index]).text(),
                                    angular.element(angular.element(this).find('li')[index])[0].id);
                                if (keycode == key.enter) {
                                    e.preventDefault();
                                }
                            } else {
                                if (keycode == key.enter) {
                                    scope.select();
                                }
                            }
                            scope.setIndex(-1);
                            scope.$apply();

                            break;
                        case key.esc:
                            // disable suggestions on escape
                            scope.select();
                            scope.setIndex(-1);
                            scope.$apply();
                            e.preventDefault();
                            break;
                        default:
                            return;
                    }

                });
            },
            template: '\
            <div class="autocomplete {{ attrs.class }}" id="{{ attrs.id }}">\
              <input\
                type="text"\
                ng-model="modelName"\
                placeholder="{{ attrs.placeholder }}"\
                class="{{ attrs.inputclass }}"\
                id="{{ attrs.inputid }}"\
                ng-required="{{ autocompleteRequired }}" />\
                <input\
                type="hidden"\
                 name="{{name}}" ng-value="{{ val }}"\
                />\
              <ul ng-show="completing && (Items | filter:searchFilter).length > 0">\
                <li\
                  \
                  ng-repeat="item in Items | filter:searchFilter | orderBy:\'toString()\' track by $index"\
                  index="{{ $index }}"\
                  id="{{item.value}}"\
                  val="{{ item.title }}"\
                  ng-class="{ active: ($index === selectedIndex) }"\
                  ng-click="select(item.title,item.value)"\
                  ng-bind-html="item.title | highlight:searchParam">\
                  </li>\
              </ul>\
            </div>'
        };
    });

    app.filter('highlight', ['$sce', function ($sce) {
        return function (input, searchParam) {
            if (typeof input === 'function') return '';
            if (searchParam) {
                var words = '(' +
                        searchParam.split(/\ /).join(' |') + '|' +
                        searchParam.split(/\ /).join('|') +
                        ')',
                    exp = new RegExp(words, 'gi');
                if (words.length) {
                    input = input.replace(exp, "<span class=\"highlight\">$1</span>");
                }
            }
            return $sce.trustAsHtml(input);
        };
    }]);

    app.directive('suggestion', function () {
        return {
            restrict: 'A',
            require: '^autocomplete', // ^look for controller on parents element
            link: function (scope, element, attrs, autoCtrl) {
                element.bind('mouseenter', function () {
                    autoCtrl.preSelect(attrs.val);
                    autoCtrl.setIndex(attrs.index);
                });

                element.bind('mouseleave', function () {
                    autoCtrl.preSelectOff();
                });
            }
        };
    });
.autocomplete {
      width: 100%;
      position: relative;
    }

    .autocomplete input {
      font-size: 1.2em;
      width: 100%;
      padding: 0.4em;
    }

    .autocomplete ul {
      position: absolute;
      left: 0;
      width: 100%;
      border-left: 1px solid #888;
      border-right: 1px solid #888;
      border-bottom: 1px solid #888;
      z-index: 1;
    }

    .autocomplete li {
      text-align: left;
      list-style: none;
      width: 100%;
      padding: 0.4em;
      background-color: #fff;
    }

    .autocomplete li.active {
      width: 100%;
      background-color: #4bf;
    }

    .autocomplete .highlight {
      background-color: #E2E2E2;
    }

    .autocomplete li.active .highlight {
      background: #666;
      color: #fff;
    }
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <div ng-app="myApp">
    <autocomplete url="url1"   model-name="result1" attr-
                                  click-activation="true"
                                  on-type="doSomething" on-select="doSomethingElse"></autocomplete>
      <autocomplete url="url2"   model-name="result2" attr-
                                  click-activation="true"
                                  on-type="doSomething" on-select="doSomethingElse"></autocomplete>
      </div>

【问题讨论】:

    标签: javascript angularjs angularjs-directive autocomplete


    【解决方案1】:

    [$compile:nonassign] 错误

    当指令定义隔离范围属性时会发生此错误 (在指令定义的范围选项中使用 = 模式)但是 该指令与不可赋值的表达式一起使用。

    为了使双向数据绑定起作用,必须能够 将新值写回到表达式定义的路径中。

    您错过了在隔离范围内添加属性autocompleteRequired,因为您需要添加具有autocomplete-required="true"等值的autocomplete-required属性

    标记

    <s:bind path="${path}">
       <autocomplete url="${url}" name="${path}"  model-name="${path}"
            click-activation="true"
            autocomplete-required="true"
            on-type="doSomething" on-select="doSomethingElse">
       </autocomplete>
    </s:bind>
    

    【讨论】:

    • 我的问题是如何创建动态 ng-model 到模板输入元素?
    • @HJZ 指令输入将有 ${path} 并且当前实现正在做同样的事情
    • @HJZ 与click-activation="true" 相关的错误丢失了..您还需要什么?
    • @Pankaj Parkar 不抱歉 :(
    • @HJZ 你能创建一个可重现问题的 plunkr 吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-27
    • 2012-12-02
    • 2015-09-27
    • 2010-11-05
    • 1970-01-01
    • 2015-10-15
    • 1970-01-01
    相关资源
    最近更新 更多