I'm looping through a series of data and want to dynamically bind a model.
My problem is that when looping through elements, it seems as if Angular creates a new scope for each iteration, so the models are not the same in the three iterations.
I've made a simplified example of my code that does not work;
http://jsfiddle.net/Fizk/uurL65e5/
<div ng-app="">
<p ng-repeat="key in [1,2,3]">
<input type="text" ng-model="contact.name" />
{{contact}}
</p>
</div>
As opposed to the non-dynamic way that works:
http://jsfiddle.net/Fizk/d0smns1v/
<div ng-app="">
<p>
<input type="text" ng-model="contact.name" />
{{contact}}
</p>
<p>
<input type="text" ng-model="contact.name" />
{{contact}}
</p>
<p>
<input type="text" ng-model="contact.name" />
{{contact}}
</p>
</div>
The real code is a bit more complicated, and I cannot just hardcode the number of fields, since it's dynamically fetched from an api.
I've looked through tons of questions regarding dynamic model binding and looked through the documentation, but with no luck.
Can anyone shed some light on how I can make all three fields use the same model, so they'll update nicely?
Angular 1.3 added a controllerAs option which should solve all your issues when dealing with child scopes and scope inheritance.
This is considered best practice today. I've created a plunker for you.
<div ng-app="myApp" ng-controller="myCtrl as vm">
<p ng-repeat="key in [1,2,3]">
<input type="text" ng-model="vm.contact.name" />
{{contact}}
</p>
</div>
<script>
angular.module("myApp", []).controller("myCtrl", function() {
var vm = this;
// use vm.value instead of $scope.value
});
</script>
I highly recommend reading this article: Understanding Scopes.
And to understand the new controllerAs syntax you should check out: Exploring Angular 1.3: Binding to Directive Controllers.
If you assign contact to be an object in the parent scope before creating the child scope (I'm using ng-init to do this but it would make more sense in a controller), it will work as the child scopes will inherit the reference to the same object.
http://jsfiddle.net/uurL65e5/1/
<div ng-app="" ng-init="contact = {}">
<p ng-repeat="key in [1,2,3]">
<input type="text" ng-model="contact.name" />
{{contact}}
</p>
</div>
you need to define your model before, usually you define your model in your controller but for instance this works :
<div ng-app="">
<input type="text" ng-model="contact.name" />
<p ng-repeat="key in [1,2,3]">
<input type="text" ng-model="contact.name" />
{{contact}}
</p>
</div>
Just use $parent
<div ng-app="">
<p ng-repeat="key in [1,2,3]">
<input type="text" ng-model="$parent.contact.name" />
{{contact}}
</p>
</div>
Fiddle - http://jsfiddle.net/ujmd0pc9/
Related
Im trying to pull the ngModel object for form validation but upon serving I'm getting an error at my template variable.
here is the code:
<form>
<div class="form-group">
<label for="courseName">Course Name</label>
<br />
<input type="text" id="courseName" ngModel name="courseName" #courseName="ngModel" required />
<br />
<div *ngIf="!courseName.valid" class="alert alert-danger">field is required</div>
<label for="category">Category</label>
<br />
<select name="category" id="category" class="form-control-lg" ngModel>
<option value="test">poop</option>
</select>
<div class="checkbox">
<label for="checkbox">
<input id="checkbox" type="checkbox" name="moneyBack" />30-day money back guarantee
</label>
</div>
</div>
<button class="btn btn-primary">Submit</button>
</form>
EDIT
After testing, I found that the problem arises when I assign the template variable, as well as I noticed that the "#" symbol in my code editor is not the same color as the tutorial I'm watching meaning that the editor isn't recognizing the syntax so its possible that this syntax is either deprecated (which is possible, the tutorial I'm following seems to be a little older) or I am implementing it wrong, what is the correct way to declare a template variable?
OR more importantly lol
what is the correct way to access the form-control object so I can get the .value, .pristine, .touched properties?
<#templateVariable ngModel name="test"> **No Error**
<#templateVariable="ngModel" ngModel name="test>**ERROR**
EDIT
EDIT
The issue was with naming ngModel and the field the same same
EDIT
here is a screenshot of the error
here is a screenshot of the import of ngModel
There are few things that you can check:
Import FormModule in app.module.ts file.
Change your code in the following way
<input type="text" id="courseName" [(ngModel)] = "courseName" name="courseName" #course="ngModel" required/>
[(ngModel)] and reference name must be different. If you put both same name to 'courseName' you will get reference error!
Use this way
[(ngModel)]="courseName"
I'm running into an odd issue while trying to set ng-disabled on my 'Save' button in the code below. I want my input field to be required and to be a non-negative number. This code works great as structured, but when I remove that second form below my table ng-disabled no longer works. Why does ng-disabled depend on this completely unnecessary additional form which I purely added for temporary testing purposes?
<div>
<table>
<tr>
<td><p>Limit</p></td>
<td><p>1,000,000</p></td>
<form class="form-inline" name="form">
<td>
<div class="form-group">
<input type="text" ng-pattern="/^[0-9][0-9]*$/" class="form-control" ng-if="limit.saveAllowed" ng-model="limit.user.points"
ng-required="true"></input>
</div>
<p ng-if="!limit.saveAllowed">{{limit.user.points}}</p>
</td>
<td ng-if="limit.saveAllowed">
<div class="form-group">
<button class="btn btn-success" type="submit" ng-click='limit.setLimit(limit.user.points)' ng-disabled="form.$invalid">Save</button>
</div>
</td>
</form>
</tr>
</table>
<form class="form-inline" name="form">
<input type="text" ng-model="limit.user.points"
ng-required="true"></input>
</form>
</div>
Note: I am using the controllerAs syntax so limit refers to my controller. Further, this HTML all resides within a custom directive's template. I don't know if that additional info is helpful, but I'm stumped.
Forms can not be nested directly inside a <tr>(see this).
Simply wrap the form with a <td> (or just move it outside the <table>) and things should work.
I'm developing a dotnetnuke 7 module using AngularJS.
Here is my code in View.ascx:
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<h2>Validation Example</h2>
<form ng-app="myApp" ng-controller="validateCtrl"
name="myForm" novalidate>
<p>Username:<br>
<input type="text" name="user" ng-model="user" required>
<span style="color:red" ng-show="myForm.user.$dirty && myForm.user.$invalid">
<span ng-show="myForm.user.$error.required">Username is required.</span>
</span>
</p>
<p>Email:<br>
<input type="email" name="email" ng-model="email" required>
<span style="color:red" ng-show="myForm.email.$dirty && myForm.email.$invalid">
<span ng-show="myForm.email.$error.required">Email is required.</span>
<span ng-show="myForm.email.$error.email">Invalid email address.</span>
</span>
</p>
<p>
<input type="submit"
ng-disabled="myForm.user.$dirty && myForm.user.$invalid ||
myForm.email.$dirty && myForm.email.$invalid">
</p>
</form>
<script>
var app = angular.module('myApp', []);
app.controller('validateCtrl', function ($scope) {
$scope.user = 'John Doe';
$scope.email = 'john.doe#gmail.com';
});
</script>
The code is copied from W3School but it seems that the AngularJS is not working well. Here is my View's screenshot:
I have tried an AngularSJ simple example and it working good likes:
<div ng-app="" ng-init="firstName='John'">
<p>Name: <input type="text" ng-model="firstName"></p>
<p>You wrote: {{ firstName }}</p>
</div>
Why is my first block of code not working? Are there any issues regarding the "form" tag in my module?
The problem is that DNN already includes the <form> tag (via Default.aspx), and nesting form tags isn't permitted.
A solution is to use <ng-form> as a substitute. eg:
...
<div ng-app="myApp" ng-controller="validateCtrl">
<ng-form name="myForm">
<p>Username:
<input type="text" name="user" ng-model="user" required>
<span style="color:red" ng-show="myForm.user.$dirty && myForm.user.$invalid">
<span ng-show="myForm.user.$error.required">Username is required.</span>
</span>
</p>
<p>
<input type="submit" ng-disabled="myForm.user.$dirty && myForm.user.$invalid">
</p>
</ng-form>
</div>
...
Also, you'll want to turn off HTML 5 default form validation since you're using Angular validation methods (this query works in DNN):
$(function() {
$('#Form').attr('novalidate', 'novalidate');
});
Further reading:
ngForm directive in AngularJS docs
Forms: Official AngularJS docs have better examples
Please be aware that the W3School example does not use best practices, and will introduce bad habits for the inexperienced (specifically, it doesn't include a '.' in ng-model which help avoid potential parent/child scope issues down the track. See the Forms examples in the link for a more robust approach.)
The form tag is certainly an issue when running within DNN/ASP.NET Webforms. I did a tutorial on DNNHero.com on building Angular applications as DNN modules.
Two pieces of advice:
Use a div tag to add your angular controller directives:
<div id="userForm" ng-controller="validateCtrl" ng-init="init(<%=this.ModuleId%>)">
Don't hard-code the ng-app directive in your html. Instead use the bootstrap method for attaching your app.
<script type="text/javascript">
angular.element(document).ready(function () {
var moduleContainer = document.getElementById('userForm');
angular.bootstrap(moduleContainer, ["myApp"]);
});
</script>
Can you do what you need to do with DNN through its API? It is best to think of Angular applications as completely disconnected from the server, like an iOS or Android application would be. Templating through DNN and Angular is inviting pain. Who knows what your rendered HTML looks like - I'm sure DNN/ASP.NET mangles it.
If you can leverage DNN's API the right mindset would be "I'm building an Angular application that happens to use DNN for persistence".
In the code below the 2nd checkbox does not work when the 1st checkbox is clicked.
http://plnkr.co/edit/JF0IftvWx7Ew3N43Csji?p=preview
HTML:
<html ng-app="App">
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular-animate.min.js"></script>
<link rel="stylesheet" type="text/css" href="animations.css" />
<script type="text/javascript" src="script.js"></script>
</head>
<body>
Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
Show when checked:
<span ng-if="checked==true" class="animate-if">
<input type="checkbox" ng-model="checked1" ng-init="checked1=true" />
</span>
<br>
<span ng-if="checked1==true" class="animate-if">
test <input type="checkbox" ng-model="checked2" ng-init="checked2=true" />
</span>
</body>
</html>
As noted ng-if creates it's own scope.
You're setting checked1 inside what I'm calling "Inner Scope 1". Then using it in "Outer Scope". "Outer Scope" can't see into "Inner Scope 1" so javascript creates a new variable checked1 on "Outer Scope". Now you have two entirely different variables both calledchecked1- one on "Outer Scope" and one on "Inner Scope1". This is not what you want.
To fix this you need to set checked1 on the same scope as you'll use it- "Outer Scope". "Outer Scope" is the parent of "Inner Scope1" so we can use $parent.checked1 like so:
<input type="checkbox" ng-model="$parent.checked1" ng-init="checked1=true" />
Now there's only one copy of checked1- the one on "Outer Scope". And it works, check it out on this updated plunker: http://plnkr.co/edit/XaPWYvYLrFRZdN2OYn6x?p=preview
ngIf creates a different scope. Use $parent:
<span ng-if="checked==true" class="animate-if">
<input type="checkbox" ng-model="$parent.checked1"
ng-init="$parent.checked1=true" />
</span>
<br>
<span ng-if="checked1==true" class="animate-if">
test <input type="checkbox" ng-model="$parent.checked2"
ng-init="$parent.checked2=true" />
</span>
Plunk
You can use ngShow instead of ngIf. ngShow helps because it doesn't create child scope. See result here.
The issue comes from the fact that you are using primitive type (boolean) for your data model. Generally it is admit that you should not use primitive types for two way data binding. If you want to know why I encourage you to read this article important part being :
This means that, if a property changes within a local scope, the original/parent version of the property isn’t updated with those changes.
In your case what is happening is :
- You are using primitive type boolean
- ngIf creates/destroy its scope while inheriting from its parent scope
- Every change made within the ngIf local scope is never propagated to the parent.
- Hence no update on the nested if.
To fix the issue please use a JS object to hold your check boxes values as follows :
<body>
Click me:
<input type="checkbox" ng-model="checkboxes.first" ng-init="checkboxes = {first : true, second:true, third:true}" />
<br/>Show when checked:
<span ng-if="checkboxes.first==true" class="animate-if">
<input type="checkbox" ng-model="checkboxes.second" />
</span>
<br>
<span ng-if="checkboxes.second==true" class="animate-if">
test <input type="checkbox" ng-model="checkboxes.third"/>
</span>
</body>
Note that the check boxes are now bound to boolean values inside a js object.
Modifyied working Plunker over here
I think the issue has to do with the fact that you are not nesting the ng-if. The two span tags are siblings.
So, my theory is that when the compile phase occurs, the ng-if that the Third depends on cannot be executed. So, it cannot build up the entire Third part of the template.
This plunker is working (with nested elements).
<div>First <input type="checkbox" ng-model="first" ng-init="first = true"/></div>
<div ng-if="first">
Second <input type="checkbox" ng-model="second" ng-init="second = true"/>
<div ng-if="second">
Third <input type="checkbox" ng-model="third" ng-init="third = true"/>
</div>
</div>
If I change my plunker to this, it is not working (with siblings):
<div>First <input type="checkbox" ng-model="first" ng-init="first = true"/></div>
<div ng-if="first">
Second <input type="checkbox" ng-model="second" ng-init="second = true"/>
</div>
<div ng-if="second">
Third <input type="checkbox" ng-model="third" ng-init="third = true"/>
</div>
<form class="well form-search">
<input type="text" id="reference" ng-model="reference" name="reference" class="input-large search-query">
<button ng-click="getDetail(reference)" class="btn">Search</button>
</form>
I have a function that updates the reference input and gives it focus, however the ng-click function on the button fails to fire
New to angular
function reffocus(ovalue) {
$("#reference").val(ovalue);
$("#reference").focus();
}
Something like this will work (and here is a plunker):
*Note: if you already have ng-app and ng-controller setup you don't need to set them here.
<form class="well form-search" ng-app="myApp" ng-controller="Ctrl">
<input type="text" id="reference" ng-model="reference" name="reference" class="input-large search-query">
<button ng-click="getDetail()" class="btn">Search</button>
</form>
Javascript:
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl', function($scope) {
$scope.getDetail = function() {
// $scope.reference will be initialized by ng-model
console.log($scope.reference);
}
});
Notice how Davin did not use jQuery in his solution. If you are new to angular, you need to temporarily drop jQuery. It is a crutch and will really slow down your learning process. You might get a little frustrated but hang in there. You can do a lot of jQuery stuff with angular. I know it really helped me in the beginning. Now I use jQuery only when it is appropriate.