Данные не переходят в директиву с использованием ngModel и преобразования данных с помощью $ formatters и $ parsers

0

У меня возникают проблемы с директивой, не обновляющейся из родительских данных, на которых она основана. Это может быть связано с указанной директивой, использующей как входы ngModel, так и преобразование данных.

Я создал пример plunker, который имитирует структуру приложения, над которым я работаю:

Пример plnkr

Точная проблема: при нажатии "Shuffle Numbers" свойства объекта myData в корневой области обновляются. Однако дочерние директивы myObjectInput, содержащие преобразованные данные на основе myData, не обновляются.

Любая помощь была бы оценена по достоинству! Код, вставленный ниже, если вы предпочитаете просеивать его, а не смотреть на пример plunker.


index.html

<!DOCTYPE html>
<html>

  <head>
    <script data-require="[email protected]" data-semver="1.4.5" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="main.js"></script>
    <script src="my-object-inputs.js"></script>
  </head>

  <body>
    <h1>Data Binding with Directive Transformation</h1>

    <p>Problem: When "Shuffle Numbers" is clicked, properties in the myData object in the root scope are updated. However, the myObjectInput child directives, which contain transformed data based on myData, do not update.<p>

    <div ng-app="myApp" ng-controller="MainCtrl">
      <div class="left">
        <h2>Directives Data (editable)</h2>
        <div class="group" ng-repeat="group in myData.myGroups">
          <h4>{{ group.myGroupName }} 
            <a class="btn" ng-click="duplicateGroup( group )">+</a>
            <a class="btn" ng-click="removeGroup( group )">-</a>
          </h4>
          <my-object-inputs ng-model="group.myObjs"></my-object-inputs>
        </div>
        <div class="group"><a class="btn" ng-click="shuffle()">Shuffle Numbers</a></div>
      </div>
      <div class="right">
        <h2>Root Data</h2>
        <textarea disabled='disabled'>{{ myData | json }}</textarea>
      </div>
    </div>
  </body>
</html>

main.js

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

myApp.controller('MainCtrl', ['$scope', '$filter',
  function ($scope, $filter) {
    $scope.myData = {
      myGroups : [
        {
          myGroupName: 'First',
          myObjs : [
            {
              order: 0,
              number : 'one'
            },
            {
              order: 0,
              number : 'two'
            },
            {
              order: 1,
              number : 'three'
            },
            {
              order: 1,
              number : 'four'
            }
          ]
        },
        {
          myGroupName: 'Second',
          myObjs : [
            {
              order: 0,
              number : 'five'
            },
            {
              order: 0,
              number : 'six'
            },
            {
              order: 1,
              number : 'seven'
            },
            {
              order: 1,
              number : 'eight'
            }
          ]
        },
        {
          myGroupName: 'Third',
          myObjs : [
            {
              order: 0,
              number : 'nine'
            },
            {
              order: 0,
              number : 'nine'
            },
            {
              order: 1,
              number : 'nine'
            },
            {
              order: 1,
              number : 'nine'
            }
          ]
        }
      ]
    };

    $scope.shuffle = function() {
      // gather all numbers
      var numbersArr = [];
      for ( var i = 0; i < $scope.myData.myGroups.length; i++ ) {
        for ( var j = 0; j < $scope.myData.myGroups[i].myObjs.length; j++ ) {
          numbersArr.push( $scope.myData.myGroups[i].myObjs[j].number );
        }
      }

      // shuffle list of all numbers
      numbersArr = numbersArr.sort(function() { return 0.5 - Math.random() });

      // assign shuffled numbers to original data
      var k = 0;
      for ( var i = 0; i < $scope.myData.myGroups.length; i++ ) {
        for ( var j = 0; j < $scope.myData.myGroups[i].myObjs.length; j++ ) {
          $scope.myData.myGroups[i].myObjs[j].number = numbersArr[k++];
        }
      }
    }

    $scope.duplicateGroup = function( group ) {
      $scope.myData.myGroups.push( angular.copy( group ) );
      $scope.myData.myGroups[$scope.myData.myGroups.length-1].myObjs = angular.copy(group.myObjs);
    };

    $scope.removeGroup = function( group ) {
      if ( $scope.myData.myGroups.length > 1 ) {
        var index = $scope.myData.myGroups.indexOf( group );
        $scope.myData.myGroups.splice(index, 1);
      }
    };
  }
]);

мой объектно-inputs.html

<ul>
    <li class="my-object-inputs" ng-repeat="arr in transformedObjs">
        <input type="text" ng-repeat="item in arr" ng-model="item.number" />
    </li>
</ul>

мой объектно-inputs.js

myApp.directive('myObjectInputs', function() {
  var controller = ['$scope', function ($scope) {
    $scope.transformedObjs = [];
    $scope.showTransformedObjs = function() {
      console.log('transformedObjs = ');console.dir($scope.transformedObjs);
    };
  }];

  return {
    restrict: 'E',
    replace: true,
    require: 'ngModel',
    templateUrl: 'my-object-inputs.html',
    controller: controller,
    scope: {
      ngModel: '='
    },
    link: function( $scope, element, attrs, ngModelCtrl ) {

      // transform to new data format
      ngModelCtrl.$formatters.push( function(modelValue) {
        var transformedData = [[],[]];

        for (var i=0; i<modelValue.length; i++) {
          var transformed;
          if (modelValue[i].number == "zero") { transformed = 0 }
          else if (modelValue[i].number == "one") { transformed = 1 }
          else if (modelValue[i].number == "two") { transformed = 2 }
          else if (modelValue[i].number == "three") { transformed = 3 }
          else if (modelValue[i].number == "four") { transformed = 4 }
          else if (modelValue[i].number == "five") { transformed = 5 }
          else if (modelValue[i].number == "six") { transformed = 6 }
          else if (modelValue[i].number == "seven") { transformed = 7 }
          else if (modelValue[i].number == "eight") { transformed = 8 }
          else if (modelValue[i].number == "nine") { transformed = 9 }
          if (transformed) {
            transformedData[ modelValue[i].order ].push({
              order: modelValue[i].order,
              number: transformed
            });
          }
        }

        return transformedData;
      });

      // transform back to original data format
      ngModelCtrl.$parsers.push( function(viewValue) {
        var untransformedData = [];

        for (var i=0; i<viewValue.length; i++) {
          for (var j=0; j<viewValue[i].length; j++) {
            var untransformed;
            if (viewValue[i][j].number == 0) { untransformed = "zero" }
            else if (viewValue[i][j].number == 1) { untransformed = "one" }
            else if (viewValue[i][j].number == 2) { untransformed = "two" }
            else if (viewValue[i][j].number == 3) { untransformed = "three" }
            else if (viewValue[i][j].number == 4) { untransformed = "four" }
            else if (viewValue[i][j].number == 5) { untransformed = "five" }
            else if (viewValue[i][j].number == 6) { untransformed = "six" }
            else if (viewValue[i][j].number == 7) { untransformed = "seven" }
            else if (viewValue[i][j].number == 8) { untransformed = "eight" }
            else if (viewValue[i][j].number == 9) { untransformed = "nine" }
            if (untransformed) {
              untransformedData.push({
                order: viewValue[i][j].order,
                number: untransformed
              })
            }
          }
        }

        return untransformedData;
      });

      // watch for updates to data
      $scope.$watch('transformedObjs', function() {
        ngModelCtrl.$setViewValue( angular.copy( $scope.transformedObjs ) );
      }, true);

      // update view
      ngModelCtrl.$render = function() {
        $scope.transformedObjs = ngModelCtrl.$viewValue;
      };
    }
  }
});

style.css

h2,h4{margin:0 0 .5em}.right,p{max-width:600px}.left,.right{clear:none;float:left}a{color:#00f;cursor:pointer}h2{font-size:1.2em}h4{font-size:1em}ul{margin:0;padding:0}li{list-style:none}input{width:50px}textarea{border:1px solid #ccc;box-sizing:border-box;height:400px;width:100%}.btn{background:#ddd;padding:0 5px}.group{margin:0 0 1em}.left{padding:0;width:250px}.right{width:calc(100% - 300px)}
Теги:
angular-ngmodel
angularjs-directive

1 ответ

1
Лучший ответ

Вам не нужен контроллер. Вы можете перейти в область действия, посмотреть ее и преобразовать из директивы.

http://plnkr.co/edit/QtBMgKVWfknlZ8m9O7uc?p=preview

 myApp.directive('myObjectInputs', function() {
  return {
    restrict: 'E',
    replace: true,
    require: 'ngModel',
    transclude: true,
    templateUrl: 'my-object-inputs.html',
    scope: {
      model: '=ngModel'
    },
    link: function( $scope, element, attrs, ngModelCtrl ) {
      var transform = function(modelValue) {
                var transformedData = [[],[]];

        for (var i=0; i<modelValue.length; i++) {
          var transformed;
          if (modelValue[i].number == "zero") { transformed = 0 }
          else if (modelValue[i].number == "one") { transformed = 1 }
          else if (modelValue[i].number == "two") { transformed = 2 }
          else if (modelValue[i].number == "three") { transformed = 3 }
          else if (modelValue[i].number == "four") { transformed = 4 }
          else if (modelValue[i].number == "five") { transformed = 5 }
          else if (modelValue[i].number == "six") { transformed = 6 }
          else if (modelValue[i].number == "seven") { transformed = 7 }
          else if (modelValue[i].number == "eight") { transformed = 8 }
          else if (modelValue[i].number == "nine") { transformed = 9 }
          if (transformed) {
            transformedData[ modelValue[i].order ].push({
              order: modelValue[i].order,
              number: transformed
            });
          }
        }

        return transformedData;
      }

      var untransform = function (viewValue) {
        var untransformedData = [];

        for (var i=0; i<viewValue.length; i++) {
          for (var j=0; j<viewValue[i].length; j++) {
            var untransformed;
            if (viewValue[i][j].number === 0) { untransformed = "zero" }
            else if (viewValue[i][j].number == 1) { untransformed = "one" }
            else if (viewValue[i][j].number == 2) { untransformed = "two" }
            else if (viewValue[i][j].number == 3) { untransformed = "three" }
            else if (viewValue[i][j].number == 4) { untransformed = "four" }
            else if (viewValue[i][j].number == 5) { untransformed = "five" }
            else if (viewValue[i][j].number == 6) { untransformed = "six" }
            else if (viewValue[i][j].number == 7) { untransformed = "seven" }
            else if (viewValue[i][j].number == 8) { untransformed = "eight" }
            else if (viewValue[i][j].number == 9) { untransformed = "nine" }
            if (untransformed) {
              untransformedData.push({
                order: viewValue[i][j].order,
                number: untransformed
              });
            }
          }
        }

        return untransformedData; 
      }

      // watch for updates on parent to data
      $scope.$watch('model', function() {
        $scope.transformedObjs = angular.copy(transform($scope.model));
      }, true);

      // watch for updates on directive to data
      $scope.$watch('transformedObjs', function() {
        $scope.model = angular.copy(untransform($scope.transformedObjs));
      }, true);
    }
  }
});
  • 0
    Это очень хорошо работает для исправления директивы parent-child, но есть две проблемы: 1) Это нарушает привязку данных от директивы child к parent (если вы редактируете числа в полях, myData не обновляется) 2) В реальном приложении В контроллере директивы my-object-input есть пара функций. Возможно, я смогу перенести их к родителю, но идеальное решение здесь оставило бы этот контроллер на месте.
  • 0
    Я обновил код, чтобы отразить пропущенный мной сценарий.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню