指令模板上的AngularJS ng-model

时间:2015-04-15 15:50:16

标签: angularjs angularjs-directive angularjs-scope

我有以下指令:

.directive('radioList', function ($ionicModal) {
    return {
        restrict: 'A',

        scope: {
            selectedItem: '=selectedItem',
            items: '=items'
        }
    };

使用以下模板html:

    <div ng-repeat="item in items">
        <ion-radio ng-model="selectedItem" ng-value="item">{{item.toString()}}</ion-radio>
    </div>

虽然绑定在指令中,但ng-model不能在指令隔离范围或父范围上正确更新。

如果我这样做:

.directive('radioList', function ($ionicModal) {
    return {
        restrict: 'A',

        scope: {
            selectedItemInternal: '=selectedItem',
            items: '=items'
        },
        link: function (scope, element, attr) {
            Object.defineProperty(scope, 'selectedItem', {
                get: function () {
                    return scope.selectedItemInternal;
                },
                set: function (value) {
                    scope.selectedItemInternal = value;
                }
            });
        }
    };

一切正常,我的selectItems的setter被调用。

好像是Angular中的一个错误?

更新:这是我的完整指令:

.directive('radioList', function ($ionicModal) {

    return {
        restrict: 'A',

        scope: {
            selectedItemInternal: '=selectedItem',
            items: '=items',
            header: '=header',
            showCancel: '=showCancel',
            listHide: '&listHide',
            doneText: '=doneText'
        },

        link: function (scope, element, attr) {

            element.css('cursor', 'pointer');

            var modal;

            scope.hide = function (result) {
                modal.remove();
                modal = null;
                if (scope.listHide) {
                    scope.listHide()(result, scope.selectedItem);
                }
            };

            // allow deselecting a radio button
            var isDeselecting = false; // event fires again after scope.selectedItem changes
            var hasChanged = false;
            scope.click = function (event) {
                if (!isDeselecting) {
                    hasChanged = scope.selectedItem != angular.element(event.target).scope().item;
                    isDeselecting = true;
                } else {
                    if (!hasChanged) {
                        scope.selectedItem = null;
                    }
                    isDeselecting = false;
                    hasChanged = false;
                }
            };

            // required to handle that click only fires once when double clicking
            scope.doubleClick = function () {
                isDeselecting = false;
                hasChanged = false;
            };

            // necessary due to a bug in AngularJS binding ng-model in the template
            Object.defineProperty(scope, 'selectedItem', {
                get: function () {
                    return scope.selectedItemInternal;
                },
                set: function (value) {
                    scope.selectedItemInternal = value;
                }
            });

            element.on('click', function () {

                $ionicModal.fromTemplateUrl('templates/radio-list.html', {
                    scope: scope
                }).then(function (m) {
                    modal = m;

                    // due to bug in ionic framework, scroll won't work unless we do this
                    ionic.keyboard.hide();

                    modal.show();
                });

            });

            element.on('$destroy', function () {
                if (modal) {
                    modal.remove();
                    modal = null;
                }
            });

        }
    };
})

这是我的radio-list.html

<ion-modal-view>

    <div class="text-center" ng-show="header">
        <h5>{{header}}</h5>
    </div>

    <ion-content style="position: absolute; top: {{showCancel ? '30px': '0'}}; bottom: {{showCancel ? 103 : 53}}px; border: 1px grey;border-bottom-style: solid; width: 100%;">
        <div ng-repeat="item in items">
            <ion-radio ng-click="click($event)" ng-dblclick="doubleClick()" ng-model="selectedItem" ng-value="item">{{item.toString()}}</ion-radio>
        </div>
    </ion-content>

    <a class="button button-full button-energized" ng-show="showCancel"
       style="position: absolute; bottom: 50px; width: 100%; margin-top: 2px; margin-bottom: 2px;"
       ng-click="$event.stopPropagation();hide(false)">Cancel</a>

    <a class="button button-full button-energized"
       style="position: absolute; bottom: 0; width: 100%; margin-top: 2px; margin-bottom: 2px;"
       ng-click="$event.stopPropagation();hide(true)">{{doneText || 'Done'}}</a>

</ion-modal-view>

以下是用法:

                <label class="item item-input validated" radio-list items="locations"
                       selected-item="account.locationPreference">
                    <span class="input-label">LOCATION</span>
                    <input type="hidden" ng-model="account.locationPreference" name="locationPreference"
                           required="required">
                    <span ng-show="account && !account.locationPreference"
                          class="placeholder value">Neighborhood</span>

                    <span class="input-value">{{account.locationPreference}}</span>
                </label>

1 个答案:

答案 0 :(得分:1)

了解范围继承和设置值时产生的somewhat unintuitive behavior,例如ng-model所做的事情。

在这种情况下,ng-repeat为每次迭代创建一个子范围,所以当你有

ng-model="selectedItem"

您正在子范围上创建selectedItem属性并在那里设置值 - 而不是在指令的隔离范围上。

作为快速解决方法,您可以直接设置$parent.selectedItem

<div ng-repeat="item in items">
  <ion-radio ng-model="$parent.selectedItem" ng-value="item">{{item.toString()}}
  </ion-radio>
</div>

<强> Demo 1

或者,您可以使用bindToControllercontrollerAs语法:

return {
  // ...
  scope: {
    // whatever you have
  },
  bindToController: true,
  controllerAs: vm,
  controller: angular.noop
}

并使用模板中的别名:

<div ng-repeat="item in vm.items">
  <ion-radio ng-model="vm.selectedItem" ng-value="item">{{item.toString()}}
  </ion-radio>
</div>

<强> Demo 2