Expose ng-form in scope and reference in ng-init - angularjs

I am attempting to do something like this:
<div ng-form="testForm" ng-init="$ctrl.doStuffWithForm(testForm)>
Where I define a form/publish the form to the current scope with the name testForm and pass that form object to $ctrl.doStuffWithForm() in the ng-init. However, what appears to be happening is that, at the time of the ng-init, the form creation and/or the creation of testForm in the scope has not happened yet (other testing indicates to me that testForm does point to a form controller at a later point in the Angular lifecycle).
Is there any way for me to pass the form controller to a method in the ng-init like this? Or should I be doing it another way? Is there an attribute similar to ng-init, but that is used later in the lifecycle? What I am trying to do is basically an ng-repeat on an element with an ng-form, and pass each form to a controller method when each element is initialized.

Avoid using $ng-init directive. Simply assign the ng-form to a property of the $ctrl object:
<div ng-form="$ctrl.testForm">
{{$ctrl.testForm.$dirty}}
</div>

I was able to achieve my desired results by putting the ng-init in a child element of the ng-form element:
<div ng-form="testForm">
<div ng-init="$ctrl.doStuffWithForm(testForm)></div>
</div>

Related

Can i use ng-model with <p> tag?

I am new to angular-js and i think this is a basic question how to get the value from a paragraph tag in angular-js? I tried with using ng-model but doesn't giving the value.Can anyone help me?
ng-model is working for two way binding. p tag is not supported for ng-model. if you want to bind p tag then you can use ng-bind or {{}}.
<p ng-bind="test"></p>
plunker code here with ng-bind
No. You can not use ng-model for <p> tag. If you want to show the value of scope variable inside <p> tag, Then you can use expressions.
Example
<p> {{ variable }} </p>
This will show the value of variable into paragraph.
Ng-model is not supported for P,H tags,,,
if you want to bind tags then use ng-class
<p ng-class="localUser.yourdata">{{localUser.yourdata}}</p>
<h1 ng-class="localUser.yourdata">{{localUser.yourdata}}</h1>
hope this is help full
You can only directly mutate that ng-model in the controller to which the view is binded via an input tag. You could use {{}} or ng-bind for one way binding, from controller to view. But to achieve two way data binding you need an input tag or other alternatives like select.
In your case if you want to read changes inside other tags like p tags you need to watch those tags inside the scope, using $scope.$watch and listen for updates have a callback function execute on change.
So basically you can't achieve what your trying to do here. This is because p tags have static values and they do not change, so one way data binding is the best option here, from controller to view during the initialization of that tag. No point, later, would you be able to change the value inside the p tag, thus no need for two data binding here. Thus ng-model is not supported. Only ng-bind or {{}}.

Variable value as directive/controller name inside template (with $compile/$interpolate)?

I am creating a directive in which template I need to use the a scope's variable value as the name of the directive (or alternatively controller) to load.
Say I have a directive widget that has a template called widget.html which looks like:
<div class="widget widget.type" {{widget.type}} ng-controller="widget.type">
<div class="navBar">
<div ng-include="widget.type + '-t.html'"></div>
<i class="fa fa-close"></i>
<hr>
</div>
<div ng-include="widget.type + '-f.html'"></div>
</div>
Now widget.type is not getting evaluated in the first line. It works fine for ng-include. Say widget.type's value is weather. The first line should then be interpolated first to look like (doesn't matter if class attribute, widget.type-attr or ng-controller is interpolated)
<div class="widget" weather>
and then compiled to include the weather directive.
How can I get widget.type interpolated in the template?
Not an option is to use ng-include to load the directive. I need to use one common template for the widget and want to add/override/extend the base directive with additonal functionality/Variables.
If this is not the way to achieve that, is there a way to extend a directive the OOP-way?
See the plunkr
You can only place interpolation expressions in text nodes and attribute values. AngularJS evaluates your template by first turning it into DOM and then invoking directive compilation, etc. If you try to place {{...}} instead of attribute name, you'll just end up with messed-up DOM.
If you really need to replace a whole directive based on $scope variable value, you'll need to create a directive for application of other directives and do some heavy lifting with $compile (you'll have to completely re-compile the template each time the value changes). I'd recommend trying to find other designs solving your situation before attempting this.
For adjusting your template based on element attributes, see this answer.

Parent scope after changing view template using ng-include

I have a controller who changes its view using 2 ng-include( tried both file and script). One for view and other for edit screen.
<div class="content-area">
<div class='row'>
<div class="cs-info">
<fp-info-bar config="headerConfig"></fp-info-bar>
<ng-include src="currentTemplate"></ng-include>
</div>
</div>
View template do not have inputs. The edit template have a form, 4 ng-form which them self contain inputs and custom directives.
<ng-form name='premisesAddressForm' attempt>
<div has-permission='CustomerDetailEditOverrideAddressValidation'>
<input type="checkbox" ng-model="isPremisesAutoCorrectDisabled"><span>Turn off Auto Correct</span>
</div>
<fp-address-edit
states="states"
ng-model='customer.PremisesAddress'
on-change="validateAddress(customer.PremisesAddress, premisesAddressForm, isPremisesAutoCorrectDisabled)">
</fp-address-edit>
</ng-form>
Data gets successfully populated and other things works fine too with in directive in edit mode.
Problem: When i try to save data on parent controller and check scope, i do not find any form(premisesAddressForm) or other input fields on edit template. i.e. this $scope.premisesAddressForm do not exist. Just to make clear, the save button is on same template and is bind to function on parent controller, no problems with that. I tried creating objects with in parent controller like this but no use.
$scope.forms = {};
<ng-form name='froms.premisesAddressForm' attempt>
I know ng-include create its own scope (inherit form parent) but i am not trying to get parent objects on child so no issues there. I also read some where that after ng-include changes, Angular compiles and updates the scope. To me there lies the problem. How i supposed to make it work? If my approach is wrong all together, please feel free to point out. My objective is to use same controller for different views, with different inputs or none. Thanks
You're correct that ngInclude creates a new child scope. The problem is that the parent scope will not have access to child scoped properties. What you could do is the same thing you tried, but instead of passing the form back to the parent controller, you should pass the values back.
In your parent controller:
$scope.myObj = {
customer: {}
};
In your template:
<ng-form name='premisesAddressForm' attempt>
<div has-permission='CustomerDetailEditOverrideAddressValidation'>
<input type="checkbox" ng-model="myObj.isPremisesAutoCorrectDisabled"><span>Turn off Auto Correct</span>
</div>
<fp-address-edit
states="states"
ng-model='myObj.customer.PremisesAddress'
on-change="validateAddress(myObj.customer.PremisesAddress, premisesAddressForm, myObj.isPremisesAutoCorrectDisabled)">
</fp-address-edit>
</ng-form>
If you need for the controllers to communicate during saving (e.g. kick off validation), you could use events.

Difference between onLoad and ng-init in angular

I am learning angular. I don't understand what is difference between onLoad and ng-init for initialization of a variable. In which scope it creates this variable.
For example
<ng-include onLoad="selectedReq=reqSelected" src="'partials/abc.html'"></ng-include>
OR
<ng-include ng-init="selectedReq=reqSelected" src="partials/abc.html"></ng-include>
Please also give me some idea about isolated scope.
ng-init is a directive that can be placed inside div's, span's, whatever, whereas onload is an attribute specific to the ng-include directive that functions as an ng-init. To see what I mean try something like:
<span onload="a = 1">{{ a }}</span>
<span ng-init="b = 2">{{ b }}</span>
You'll see that only the second one shows up.
An isolated scope is a scope which does not prototypically inherit from its parent scope. In laymen's terms if you have a widget that doesn't need to read and write to the parent scope arbitrarily then you use an isolate scope on the widget so that the widget and widget container can freely use their scopes without overriding each other's properties.
From angular's documentation,
ng-init SHOULD NOT be used for any initialization. It should be used only for aliasing.
https://docs.angularjs.org/api/ng/directive/ngInit
onload should be used if any expression needs to be evaluated after a partial view is loaded (by ng-include).
https://docs.angularjs.org/api/ng/directive/ngInclude
The major difference between them is when used with ng-include.
<div ng-include="partialViewUrl" onload="myFunction()"></div>
In this case, myFunction is called everytime the partial view is loaded.
<div ng-include="partialViewUrl" ng-init="myFunction()"></div>
Whereas, in this case, myFunction is called only once when the parent view is loaded.
Works for me.
<div ng-show="$scope.showme === true">Hello World</div>
<div ng-repeat="a in $scope.bigdata" ng-init="$scope.showme = true">{{ a.title }}</div>

Cannot get textarea value in angularjs

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?
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>
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>
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>
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—what is the difference between ng-if and ng-show/ng-hide.

Resources