Подтвердить что ты не робот

Как обновить модель AngularJS от функции связывания ссылок?

То, что я пытаюсь достичь, - добавить дополнительный интерфейс для полей ввода, чтобы иметь возможность увеличивать и уменьшать числовое значение в них, нажимая кнопки + и -.

(По сути, это то, что поля ввода [type = number] имеют на chrome, но я хочу, чтобы это было совместимо с кросс-браузером, а также полностью контролировало представление во всех браузерах).

Код в поле зрения:

<input data-ng-model="session.amountChosen" type="text" min="1" class="form-control input-small" data-number-input>

Код директивы:

app.directive('numberInput', function() {
return {
    require: 'ngModel',
    scope: true,
    link: function(scope, elm, attrs, ctrl) {

        var currValue = parseInt(scope.$eval(attrs.ngModel)),
            minValue = attrs.min || 0,
            maxValue = attrs.max || Infinity,
            newValue;

        //puts a wrap around the input and adds + and - buttons
        elm.wrap('<div class="number-input-wrap"></div>').parent().append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');

        //finds the buttons ands binds a click event to them where the model increase/decrease should happen
        elm.parent().find('a').bind('click',function(e){

            if(this.text=='+' && currValue<maxValue) {
                newValue = currValue+1;    
            } else if (this.text=='-' && currValue>minValue) {
                newValue = currValue-1;    
            }

            scope.$apply(function(){
                scope.ngModel = newValue;
            });

            e.preventDefault();
        });


    }
  };

})

Это позволяет получить текущее значение модели через область. $eval (attrs.ngModel), но не может установить новое значение.

Aftermath edit: это код, который теперь работает (в случае, если вы не увидите решение этой проблемы)

app.directive('numberInput', function() {
  return {
    require: 'ngModel',
    scope: true,
    link: function(scope, elm, attrs, ctrl) {

        var minValue = attrs.min || 0,
            maxValue = attrs.max || Infinity;

        elm.wrap('<div class="number-input-wrap"></div>').parent().append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');
        elm.parent().find('a').bind('click',function(e){

            var currValue = parseInt(scope.$eval(attrs.ngModel)),
                newValue = currValue;

            if(this.text=='+' && currValue<maxValue) {
                newValue = currValue+1;    
            } else if (this.text=='-' && currValue>minValue) {
                newValue = currValue-1;    
            }

            scope.$eval(attrs.ngModel + "=" + newValue);
            scope.$apply();            

            e.preventDefault();
        });
    }
  };
})
4b9b3361

Ответ 1

ngModelController следует использовать методы вместо $eval() для получения и установки значения свойства ng-model.

parseInt() не требуется при оценке атрибута с числовым значением, потому что $eval преобразует значение в число. $eval следует использовать для установки переменных minValue и maxValue.

В директиве нет необходимости создавать дочернюю область.

$apply() не требуется, потому что методы ngModelController (в частности, $render()) будут автоматически обновлять представление. Однако, как отмечает @Harijs в комментарии ниже, $apply() необходим, если необходимо обновить другие части приложения.

app.directive('numberInput', function ($parse) {
    return {
        require: 'ngModel',
        link: function (scope, elm, attrs, ctrl) {
            var minValue = scope.$eval(attrs.min) || 0,
                maxValue = scope.$eval(attrs.max) || Infinity;
            elm.wrap('<div class="number-input-wrap"></div>').parent()
                .append('<div class="number-input-controls"><a href="#" class="btn btn-xs btn-pluimen">+</a><a href="#" class="btn btn-xs btn-pluimen">-</a></div>');
            elm.parent().find('a').bind('click', function (e) {
                var currValue = ctrl.$modelValue,
                    newValue;
                if (this.text === '+' && currValue < maxValue) {
                    newValue = currValue + 1;
                } else if (this.text === '-' && currValue > minValue) {
                    newValue = currValue - 1;
                }
                ctrl.$setViewValue(newValue);
                ctrl.$render();
                e.preventDefault();
                scope.$apply(); // needed if other parts of the app need to be updated
            });
        }
    };
});

fiddle

Ответ 2

Вы не захотите заменить переменную scope.ngModel, но значение, которое стоит за этой переменной. Вы сделали это уже, когда прочитали значение в первой строке функции ссылки:

 currValue = parseInt(scope.$eval(attrs.ngModel))
 //                         ^^^^^^^^^^^^^^^^^^^^

Если это простое значение, например myProperty, вы можете использовать его в области:

 scope[attr.ngModel] = newValue

Но это не сработает, если у вас есть выражение-значение, например container.myProperty. В этом случае (и это более общий тип, к которому вы должны стремиться) вы должны были бы оценить значение, заданное для области, например:

scope.$eval(attrs.ngModel + "=" + newValue)

Я должен признать, что часть $eval немного уродлива, как и в JavaScript с подвеской eval, но это делает трюк. Просто имейте в виду, что это может не работать таким образом, когда вы хотите установить значения String. Тогда вам придется избегать этих значений.

Надеюсь, что помогает;)

Ответ 3

.directive('numberInput', function ($parse) {
   return {
      require: 'ngModel',
      link: function (scope, elm, attrs){ 
        elm.bind("click", function() {
          var model = $parse(attrs.ngModel);
          var modelSetter = model.assign;
          scope.$apply(function() {
               modelSetter(scope, model);
          });
     }
  }
})