Max Bates
Max Bates

Reputation: 1278

angular: how to bind stringified json to textarea?

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

Answers (1)

Max Bates
Max Bates

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

Fiddle + Gist

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

Related Questions