Reputation: 1278
How do I bind a JSON as a string to a textarea so I can edit it directly?
I only want my model to propagate when it is valid.
Upvotes: 2
Views: 2232
Reputation: 1278
Update: revised version for Angular 1.3 available at http://codepen.io/maxbates/pen/AfEHz
I made a directive that does this. I will edit it if you have suggestions
As far as I know, there is no way to cancel model updates in the $parsers pipeline (and looking at the ngModelCtrl source seems to validate this) other than adding a function which errors.
Note that you do not use ng-model on the textarea directly. This solution adds its own ngModel, tied to an intermediate object, and will only propagate changes if the JSON is valid.
'use strict';
/*
example usage: <textarea json-edit="myObject" rows="8" class="form-control"></textarea>
jsonEditing is a string which we edit in a textarea. we try parsing to JSON with each change. when it is valid, propagate model changes via ngModelCtrl
use isolate scope to prevent model propagation when invalid - will update manually. cannot replace with template, or will override ngModelCtrl, and not hide behind facade
will override element type to textarea and add own attribute ngModel tied to jsonEditing
As far as I know, there is currently no way to achieve this using $parsers (other than one of the function errors and kills the pipeline)
*/
angular.module('myApp')
.directive('jsonEdit', function () {
return {
restrict: 'A',
require: 'ngModel',
template: '<textarea ng-model="jsonEditing"></textarea>',
replace : true,
scope: {
model: '=jsonEdit'
},
link: function (scope, element, attrs, ngModelCtrl) {
function setEditing (value) {
scope.jsonEditing = angular.copy(JSON2String(value));
}
function updateModel (value) {
scope.model = string2JSON(value);
}
function setValid() {
ngModelCtrl.$setValidity('json', true);
}
function setInvalid () {
ngModelCtrl.$setValidity('json', false);
}
function string2JSON(text) {
try {
return angular.fromJson(text);
} catch (err) {
setInvalid();
return text;
}
}
function JSON2String(object) {
// better than JSON.stringify(), because it formats + filters $$hashKey etc.
// NOTE that this will remove all $-prefixed values
return angular.toJson(object, true);
}
function isValidJson(model) {
var flag = true;
try {
angular.fromJson(model);
} catch (err) {
flag = false;
}
return flag;
}
//init
setEditing(scope.model);
//check for changes going out
scope.$watch('jsonEditing', function (newval, oldval) {
if (newval != oldval) {
if (isValidJson(newval)) {
setValid();
updateModel(newval);
} else {
setInvalid();
}
}
}, true);
//check for changes coming in
scope.$watch('model', function (newval, oldval) {
if (newval != oldval) {
setEditing(newval);
}
}, true);
}
};
});
Upvotes: 1