Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular JS display and edit model in textarea

I would like to be able to edit and display complex model in a <textarea> element. Here's the HTML piece for generating model's fields dynamically from JSON response:

<p>parent uuid*: </p>
<input ng-model="parentUuid" capitalize type="text" placeholder="String"
    class="form-control" style="width:200px; display: inline-block;"/> <br/>
<p>resource*:</p>
<select ng-model="childResource" ng-change="loadResourceFields(childResource)" 
    class="form-control" style="width:300px; display: inline-block;">
    <option ng-repeat="childResource in restResources">{{childResource}}</option>
</select>
<div ng-repeat="field in childFields">
    <div ng-show={{!field.isEnum}}>
        <p ng-show={{field.isRequired}}>{{field.name}}*: </p>
        <p ng-show={{!field.isRequired}}>{{field.name}}: </p>
        <input type="text" ng-model="createChildResource[field.name]"
            class="form-control" style="width:200px; display: inline-block;" placeholder="{{parseClassName(field.type)}}">
    </div>
    <div ng-show={{field.isEnum}}>
        <p ng-show={{field.isRequired}}>{{field.name}}*: </p>
        <p ng-show={{!field.isRequired}}>{{field.name}}: </p>
        <select ng-model="createChildResource[field.name]" class="form-control" style="width:auto; display: inline-block;">
            <option></option>
            <option ng-repeat="enumValue in field.enumValues" label={{enumValue.name}}>{{enumValue.ordinal}}</option>
        </select>
    </div>
</div>
<div class="preview">
    <p>Preview: </p>
    <textarea style="height:350px; width:550px; overflow:scroll;">{{createChildResource | json}}</textarea >
</div>

The output is the following:

enter image description here

But if I try to add a ngModel to a textarea element to be able to edit this values in place like this:

<div class="preview">
    <p>Preview: </p>
    <textarea ng-model="createChildResource" style="height:350px; width:550px; overflow:scroll;">{{createChildResource | json}}</textarea>
</div>

then the output is the following:

enter image description here

In both cases I can't edit my model in a textarea element.

How can this be achieved? I'd like to be able to display and edit my model inplace like in this example with a slight difference: editable-textarea="user.description" should be editable-textarea="user".

like image 266
amenoire Avatar asked Apr 01 '14 10:04

amenoire


2 Answers

I finally understand what you are trying to achieve. On the left you have a bunch of inputs and on the right (bottom), you have a textarea that arranges the inputs as properties of one object and displays them formatted as an object.

Your requirements is to allow user to edit the property values in the textarea and thus update the corresponding property value in the input.

First, as commented, convert the object to a string and then show it inside the textarea.

Next, since you need to react and update the input fields when the textarea is updated, you need to watch the value of the textarea and update the original object (the one that was converted to string).

Using an example here since your code is too complex to understand, let us say that you have the object containerObject as follows:

$scope.containerObject = {
    property_1: "Hello",
    property_2: "World"
};

Your inputs then make use of these properties:

<input ng-model="containerObject.property_1">

<input ng-model="containerObject.property_2">

Now, you wish to display this inside your textarea - you will first convert the object to string and display it as follows:

$scope.getObjectAsText = function () {
    $scope.textAreaModel = JSON.stringify($scope.containerObject);
};

And your textarea markup will look like:

<textarea ng-model="textAreaModel"></textarea>

Each time the value in the input textboxes change, the textarea also gets updated.

Now, for the other way around. When you change the textarea, to have the input textboxes get updated, you need to watch the string model (and NOT the object model):

$scope.$watch('textAreaModel', function () {
    try {
        $scope.containerObject = JSON.parse($scope.textAreaModel);
    } catch(exp) {
        //Exception handler
    };
});

You need to have the try...catch exception handler block because the user may accidentally change the contents such that on converting back to object, the result is not a suitable object (invalid property or invalid syntax).

As long as only the property value is changed, the input values will get updated correctly.

like image 110
callmekatootie Avatar answered Sep 28 '22 08:09

callmekatootie


You can also wrap it into directive like below, or check the jsfiddler

Html

<div ng-app="app" ng-controller='userCtrl' >
    <textarea obj-edit obj="user" rows='10'></textarea>
    <p ng-bind='user.name'></p>
</div>

javascript

var app = angular.module("app", []);
app.controller('userCtrl', function($scope) {
    $scope.user= {name: 'ron', ocupation: 'coder'};
});
app.directive('objEdit', function() {
    return {
        restrict: 'A',
        scope: {
            obj:'=obj'
        }, 
        link: function(scope, element, attrs) {
            element.text(JSON.stringify(scope.obj, undefined, 2));
            element.change(function(e) {
                console.log(e.currentTarget.value);
                scope.$apply(function() {
                    scope.obj = JSON.parse(e.currentTarget.value);
                });
                console.log(scope.obj);
            })
        }
    }
})
like image 27
Ron Avatar answered Sep 28 '22 06:09

Ron