数据不会滴落到使用 ngModel 的指令和使用 $formatters 和 $parsers 的数据转换
Data doesn't trickle down to directive using ngModel and a data transformation with $formatters and $parsers
我遇到了一个指令问题,该指令未从它所基于的父数据进行更新。它可能与使用 ngModel 输入和数据转换的指令有关。
我已经设置了一个模仿我正在开发的应用程序结构的 plunker 示例:
确切的问题:单击 "Shuffle Numbers" 时,更新根范围内的 myData 对象中的属性。但是,包含基于 myData 的转换数据的 myObjectInput 子指令不会更新。
如有任何帮助,我们将不胜感激!如果您宁愿筛选代码也不愿查看 plunker 示例,请粘贴在下面的代码。
index.html
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js@1.4.5" 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)}
您不需要控制器。您可以传入范围,观察它并从指令转换它。
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);
}
}
});
我遇到了一个指令问题,该指令未从它所基于的父数据进行更新。它可能与使用 ngModel 输入和数据转换的指令有关。
我已经设置了一个模仿我正在开发的应用程序结构的 plunker 示例:
确切的问题:单击 "Shuffle Numbers" 时,更新根范围内的 myData 对象中的属性。但是,包含基于 myData 的转换数据的 myObjectInput 子指令不会更新。
如有任何帮助,我们将不胜感激!如果您宁愿筛选代码也不愿查看 plunker 示例,请粘贴在下面的代码。
index.html
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js@1.4.5" 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)}
您不需要控制器。您可以传入范围,观察它并从指令转换它。
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);
}
}
});