Cannot get textarea value in angularjs

AngularjsAngular Ui

Angularjs Problem Overview


Here is my plnkr: http://plnkr.co/edit/n8cRXwIpHJw3jUpL8PX5?p=preview You have to click on a li element and the form will appear. Enter a random string and hit 'add notice'. Instead of the textarea text you will get undefined.

Markup:

<ul>
    <li ng-repeat="ticket in tickets" ng-click="select(ticket)">
         {{ ticket.text }}
    </li>
</ul>
<div ui-if="selectedTicket != null">
     <form ng-submit="createNotice(selectedTicket)">
        <textarea ng-model="noticeText"></textarea>
        <button type="submit">add notice</button>
    </form>
</div>

JS part:

$scope.createNotice = function(ticket){
   alert($scope.noticeText);
}

returns 'undefined'. I noticed that this does not work when using ui-if of angular-ui. Any ideas why this does not work? How to fix it?

Angularjs Solutions


Solution 1 - Angularjs

Your problem lies in the ui-if part. Angular-ui creates a new scope for anything within that directive so in order to access the parent scope, you must do something like this:

<textarea ng-model="$parent.noticeText"></textarea>

Instead of

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

Solution 2 - Angularjs

This issue happened to me while not using the ng-if directive on elements surrounding the textarea element. While the solution of Mathew is correct, the reason seems to be another. Searching for that issue points to this post, so I decided to share this.

If you look at the AngularJS documentation here https://docs.angularjs.org/api/ng/directive/textarea , you can see that Angular adds its own directive called <textarea> that "overrides" the default HTML textarea element. This is the new scope that causes the whole mess.

If you have a variable like

$scope.myText = 'Dummy text';

in your controller and bind that to the textarea element like this

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

AngularJS will look for that variable in the scope of the directive. It is not there and thus he walks down to $parent. The variable is present there and the text is inserted into the textarea. When changing the text in the textarea, Angular does NOT change the parent's variable. Instead it creates a new variable in the directive's scope and thus the original variable is not updated. If you bind the textarea to the parent's variable, as suggested by Mathew, Angular will always bind to the correct variable and the issue is gone.

<textarea ng-model="$parent.myText"></textarea>

Hope this will clear things up for other people coming to this question and and think "WTF, I am not using ng-if or any other directive in my case!" like I did when I first landed here ;)

Update: Use controller-as syntax

Wanted to add this long before but didn't find time to do it. This is the modern style of building controllers and should be used instead of the $parent stuff above. Read on to find out how and why.

Since AngularJS 1.2 there is the ability to reference the controller object directly instead of using the $scope object. This may be achieved by using this syntax in HTML markup:

<div ng-controller="MyController as myc"> [...] </div>

Popular routing modules (i.e. UI Router) provide similar properties for their states. For UI Router you use the following in your state definition:

[...]
controller: "MyController",
controllerAs: "myc",
[...]

This helps us to circumvent the problem with nested or incorrectly addressed scopes. The above example would be constructed this way. First the JavaScript part. Straight forward, you simple do not use the $scope reference to set your text, just use this to attach the property directly to the controller object.

angular.module('myApp').controller('MyController', function () {
    this.myText = 'Dummy text';
});

The markup for the textarea with controller-as syntax would look like this:

<textarea ng-model="myc.myText"></textarea>

This is the most efficient way to do things like this today, because it solves the problem with nested scopes making us count how many layers deep we are at a certain point. Using multiple nested directives inside elements with an ng-controller directive could have lead to something like this when using the old way of referencing scopes. And no one really wants to do that all day!

<textarea ng-model="$parent.$parent.$parent.$parent.myText"></textarea>

Solution 3 - Angularjs

Bind the textarea to a scope variable's property rather than directly to a scope variable:

controller:

$scope.notice = {text: ""}

template:

<textarea ng-model="notice.text"></textarea>

Solution 4 - Angularjs

It is, indeed, ui-if that creates the problem. Angular if directives destroy and recreate portions of the dom tree based on the expression. This is was creates the new scope and not the textarea directive as marandus suggested.

Here's a post on the differences between ngIf and ngShow that describes this well—https://stackoverflow.com/questions/19177732/what-is-the-difference-between-ng-if-and-ng-show-ng-hide.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUpCatView Question on Stackoverflow
Solution 1 - AngularjsMathew BergView Answer on Stackoverflow
Solution 2 - AngularjsmarandusView Answer on Stackoverflow
Solution 3 - AngularjsNimoView Answer on Stackoverflow
Solution 4 - AngularjsThomas O'BrienView Answer on Stackoverflow