Reputation: 3842
I have a component (a directive with isolate scope) that maps a list of objects to a list of inputs that allow to modify one property of that objects.
But I want to make that component universal. So it should accept path to deeply nested property of each object in a list that should be bound to input.
For example we have a list of people, each of those have name spoken in different languages:
var people = [
{
age: 31,
multilang_attributes: {
en: {name: 'John'},
ru: {name: 'Иван'}
}
},
{
age: 29,
multilang_attributes: {
en: {name: 'Peter'},
ru: {name: 'Пётр'}
}
},
];
I want to apply my universal component to that list of people like the following:
<input-list
items="people",
item-model="multilang_attributes[locale].name"
></input-list>
I tried to create scope property with &
that would allow me to execute expression in parent scope and then pass that expression to ngModel
, but that does not work. You can look at this attempt in plunkr.
How that task can be approached in angular?
Upvotes: 3
Views: 274
Reputation: 54649
One option would be to $parse
the accessor-string and use a getter/setter for the model. For this:
change the directive-html to:
item-accessor="'multilang_attributes.' + app.locale + '.name'"
This will make something like multilang_attributes.en.name
available.
change the directive-code to:
app.directive('inputList', function () {
return {
restrict: 'E',
scope: {
items: '=',
itemAccessor: '='
},
template: '<ul><li ng-repeat="item in items"><input ng-model="getModel(item)" ng-model-options="{ getterSetter: true }" /></li></ul>',
controller: function ($scope, $parse) {
$scope.getModel = function(item) {
return function (newValue) {
var getter = $parse($scope.itemAccessor);
if (arguments.length > 0) {
getter.assign(item, newValue);
}
return getter(item);
};
};
}
};
});
External demo: http://plnkr.co/edit/VzFrBxNcsA5BarIVr6oG?p=preview
var app = angular.module('TestApp', [])
app.controller('AppCtrl', function AppController() {
this.locales = ['en', 'fr']
this.locale = 'en';
this.people = [
{
age: 31,
multilang_attributes: {
en: {name: 'John (en)'},
fr: {name: 'John (fr)'}
}
},
{
age: 29,
multilang_attributes: {
en: {name: 'Fred (en)'},
fr: {name: 'Fred (fr)'}
}
},
];
});
app.directive('inputList', function () {
return {
restrict: 'E',
scope: {
items: '=',
itemAccessor: '='
},
template: '<ul><li ng-repeat="item in items"><input ng-model="getModel(item)" ng-model-options="{ getterSetter: true }" /></li></ul>',
controller: function ($scope, $parse) {
$scope.getModel = function(item) {
return function (newValue) {
var getter = $parse($scope.itemAccessor);
if (arguments.length > 0) {
getter.assign(item, newValue);
}
return getter(item);
};
};
}
};
});
<!DOCTYPE html>
<html>
<head>
<script data-require="[email protected]" data-semver="1.4.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" integrity="sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezHubeNikyKGVyQ==" crossorigin="anonymous">
</head>
<body ng-app="TestApp" ng-controller="AppCtrl as app">
<div class="container">
<select ng-model="app.locale" ng-options="v as v for v in app.locales"></select>
<hr>
<input-list
items="app.people"
item-accessor="'multilang_attributes.' + app.locale + '.name'"
></input-list>
<hr>
<pre>{{ app.people|json }}</pre>
</div>
</body>
</html>
note that you should propably use proper injection-syntax and controllerAs/bindToController.
Upvotes: 2