I am fairly new to AngularJS.
I am trying to bind an object to a textarea.
HTML:
I’ve just researched what I believe to be the most “proper” way of doing this, as I needed it for my https://github.com/vorburger/MUI.js... So here is a Plonker with my solution. It’s based on & is essentially a special case (i.e. an application of) the related Q How to do two-way filtering in angular.js? The added twist is that model updates should also change the textbox.. that's what the $watch / $setViewValue / $render thing does.
var app = angular.module('app', []);
app.directive('jsonText', function() {
return {
restrict: 'A', // only activate on element attribute
require: 'ngModel', // get a hold of NgModelController
link: function(scope, element, attrs, ngModelCtrl) {
var lastValid;
// push() if faster than unshift(), and avail. in IE8 and earlier (unshift isn't)
ngModelCtrl.$parsers.push(fromUser);
ngModelCtrl.$formatters.push(toUser);
// clear any invalid changes on blur
element.bind('blur', function() {
element.val(toUser(scope.$eval(attrs.ngModel)));
});
// $watch(attrs.ngModel) wouldn't work if this directive created a new scope;
// see https://stackoverflow.com/questions/14693052/watch-ngmodel-from-inside-directive-using-isolate-scope how to do it then
scope.$watch(attrs.ngModel, function(newValue, oldValue) {
lastValid = lastValid || newValue;
if (newValue != oldValue) {
ngModelCtrl.$setViewValue(toUser(newValue));
// TODO avoid this causing the focus of the input to be lost..
ngModelCtrl.$render();
}
}, true); // MUST use objectEquality (true) here, for some reason..
function fromUser(text) {
// Beware: trim() is not available in old browsers
if (!text || text.trim() === '') {
return {};
} else {
try {
lastValid = angular.fromJson(text);
ngModelCtrl.$setValidity('invalidJson', true);
} catch (e) {
ngModelCtrl.$setValidity('invalidJson', false);
}
return lastValid;
}
}
function toUser(object) {
// better than JSON.stringify(), because it formats + filters $$hashKey etc.
return angular.toJson(object, true);
}
}
};
});
app.controller('Ctrl', ['$scope',
function($scope) {
$scope.model = {};
$scope.model.data = {
"kind": "title",
"label": "ADD_TITLE",
"iconSrc": "textTitle.png",
"experimentInclude": "",
"experimentExclude": "three",
"preset": {
"compType": "richTitle",
"styleId": "txtNew"
}
};
}
]);
{{ model.data }}