Сохраните объект в ngModel с помощью директивы в AngularJS.

Я работаю с angularjs уже несколько недель и не понимаю, о чем думали дизайнеры angularjs при разработке функций $viewValue и $modelValue из ngModelController. Пример кода:

index.html:

<!DOCTYPE html>
<html>

  <head>
    <script data-require="angular.js@*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.18/angular.js"></script>
    <script src="script.js"></script>
  </head>

  <body ng-app="PlunkerApp" ng-controller="mainController">
    <listfield ng-model="termList"></listfield>
  </body>

</html>

script.js:

var plunkerModule = angular.module('PlunkerApp', []);

plunkerModule.directive('listfield', function() {
  'use strict';
  var link = function(scope, element, attrs, ngModelController) {
    console.log('listfield.link():', scope);
    ngModelController.$parsers.push(function(value) {
      console.log('listfield.model.parser:', value);
      return value ? value.join(', ') : undefined;

    });
    ngModelController.$formatters.push(function(value) {
      console.log('listfield.model.formatter:', value);
      return value ? value.split(/,\s*/) : undefined;
    });
  }
  return {
    restrict: 'E',
    link: link,
    require: 'ngModel',
    scope: {
      ngModel: '='
    },
    template: '<input type="text" ng-model="ngModel">'
  };
});

plunkerModule.controller('mainController', function($scope) {
  $scope.termList = "";
  $scope.$watchCollection('termList', function(newValue, oldValue) {
    console.log('mainController.watch.list:', newValue);
  });
});

ссылка на плункер: http://plnkr.co/edit/T5a8zEQuRyYWnpsZZV9W?p=preview

Таким образом, в этом приложении значение из элемента ввода директив записывается в глобальную область видимости, что отлично работает! Моя проблема в том, что меня не интересует «необработанное» строковое значение, мне нужен массив, сгенерированный средством форматирования, но элемент ввода все равно должен отображать строковое значение.

Как мне это сделать??

С нетерпением жду ваших ответов.


person TekTimmy    schedule 19.08.2014    source источник
comment
Интересно, что в этом фрагменте, похоже, созданы два ngModelController, причем второй (у которого нет ваших средств форматирования/парсеров) вызывается при обновлении значения модели.   -  person Sacho    schedule 19.08.2014
comment
Это означало бы, что входной атрибут ng-model должен быть удален и что я должен сам обрабатывать все события (нажатие клавиши, размытие, просмотр модели), а angularjs мне не помогает =(   -  person TekTimmy    schedule 19.08.2014


Ответы (1)


Проблема здесь в том, что и ваши теги <input>, и ваши теги <listfield> имеют ngModel, что сбивает с толку, когда и какой из них вызывается. Вы можете использовать опцию replace: true для своей директивы, чтобы удалить тег <listfield> и работать только с <input>, например:

var plunkerModule = angular.module('PlunkerApp', []);

plunkerModule.directive('listfield', function() {
  'use strict';
  var link = function(scope, element, attrs, ngModelController) {
    console.log('listfield.link():', scope);
    // Your formatters and parsers seemed to be the other way around
    // The formatter transforms Model -> View
    // Whereas the parser transforms View -> Model
    ngModelController.$formatters.push(function(value) {
      console.log('listfield.model.formatter:', value);
      return value ? value.join(', ') : undefined;

    });
    ngModelController.$parsers.push(function(value) {
      console.log('listfield.model.parser:', value);
      return value ? value.split(/,\s*/) : undefined;
    });
  }
  return {
    restrict: 'E',
    link: link,
    require: 'ngModel',
    replace: true, // Removes the <listfield> tag
    scope: {
      model: '='
    },
    template: '<input type="text" ng-model="model">'
  };
});

plunkerModule.controller('mainController', function($scope, $timeout) {
  $scope.termList = [1,2,3]
  $scope.$watchCollection('termList', function(newValue, oldValue) {
    console.log('mainController.watch.list:', newValue);
  });
  $timeout(function changeTermList() { $scope.termList = [4,5,6]}, 2000)
  // This is to demonstrate the binding used via the isolate scope(=)
});

И соответствующий HTML:

  <body ng-app="PlunkerApp" ng-controller="mainController">
    <listfield model="termList"></listfield>
  </body>

Демонстрация: http://plnkr.co/edit/G0tlC9qhW8AHa58yVSgj?p=preview

person Sacho    schedule 19.08.2014
comment
Ницца! Новым для меня является то, что ngModelController вводится, хотя ng-модель не указана. Итак, ngModelController является собственным изолированным экземпляром для этой директивы? Я попытался использовать одну и ту же модель для двух полей ввода в шаблоне, как здесь: plnkr.co/edit /VHHxjc20ZDnyii52dzjN?p=preview, который работает, но выдает сообщение об ошибке: Контроллер 'ngModel', требуемый директивой 'listfield', не может быть найден!. У вас есть идея для этого? - person TekTimmy; 19.08.2014
comment
require ищет директиву ng-model для того же элемента (<listfield>) или его родителей - поскольку такой директивы нет, вы получаете эту ошибку. Но когда вы используете замену, элемента <listfield> вообще нет. - person Sacho; 19.08.2014
comment
Спасибо =) есть ли способ привязать ng-модель к элементу html, который не является первым? Например: «‹div›‹input type=text›‹/div›». - person TekTimmy; 19.08.2014