Am trying to figure out how to check the state of a ngModel without using a form tag. I don't have wrappers is just basic input element with a ngModel.
All the examples I have found so far are for form validations and in this case, there is no form.
When i tried something like:
HTML
<input type="text" ng-model="lastname">
SCRIPT:
if($scope.lastname.$dirty) {
console.log('last name has changed');
}
I get undefined.
Is there a way to check the state of the ngModel without adding a watch directive to it? it seems it would be something basic that is part of the framework. Why wouldn't this work?
Am trying to figure out how to check the state of a ngModel without using a form tag. I don't have wrappers is just basic input element with a ngModel.
All the examples I have found so far are for form validations and in this case, there is no form.
When i tried something like:
HTML
<input type="text" ng-model="lastname">
SCRIPT:
if($scope.lastname.$dirty) {
console.log('last name has changed');
}
I get undefined.
Is there a way to check the state of the ngModel without adding a watch directive to it? it seems it would be something basic that is part of the framework. Why wouldn't this work?
Share Improve this question edited Mar 3, 2014 at 16:36 glepretre 8,1675 gold badges46 silver badges58 bronze badges asked Mar 3, 2014 at 16:09 J CastilloJ Castillo 3,3575 gold badges28 silver badges28 bronze badges 4 |3 Answers
Reset to default 22There are two ways:
1. Use ng-form
:
<span ng-form="myForm">
<input type="text" name="name" ng-model="name" required/>
</span>
Now you can access the model either at $scope.myForm.name
in your controller or with myForm.name
in your view:
var isPristine = $scope.myForm.name.$pristine;
2. Use angular.element().controller('ngModel')
(Don't do this one, bad bad bad)
Alternatively, you could hack your way around it. But this is going to be ugly, untestable, and gross:
var elem = angular.element(document.getElementById('myElement'));
var model = elem.controller('ngModel');
var isPristine = model.$pristine;
Edit: Your situation (per your comment) inside of a repeater
the only difference between my example and your is that the input field is inside a ng-repeater. Thought that wouldn't matter but I guess it does.
And now it's time to ask yourself what you're doing and why... You can still get the information you need using ng-form
, but you'll need to do some crazy stuff I wouldn't recommend:
<div ng-repeater="item in items track by $index">
<span ng-form="rptrForm">
<input type="text" name="name" ng-model="item.name" required/>
</span>
</div>
.. commence craziness:
// get the first child scope (from the repeater)
var child = $scope.$$childHead;
while(child) {
var isPristine = child.rptrForm.$pristine;
var item = child.item;
if(!isPristine) {
// do something with item
}
child = child.$$nextSibling;
}
It's probably time to rethink your strategy
I'm not sure what your end goal is, but you might want to rethink how you're going about it and why. Why do you need programmatic access to $pristine in your controller? What alternatives are there? Etc.
I, for one, would try to leverage an ng-change
event and update some flag on my item in my repeater, and leave the ng-form stuff for validation:
<div ng-repeat="item in items track by $index" ng-form="rptrForm">
<input type="text" name="name" ng-model="item.name" ng-change="item.nameChanged = true" required/>
<span ng-show="rptrForm.name.$error.required>Required</span>
</div>
If you give the
<form>
element aname
attribute, then the<form>
will be added to the$scope
object as a property.
Field controller will then be attached to theform
property.
As weird as it could seem, you have to define an enclosing form
with a name
attribute like so:
<form name="myForm">
<input type="text" name="lastName" ng-model="lastname">
</form>
and call the property with:
$scope.myForm.lastname.$dirty
Indeed, ngModelController
(field) is attached to ngFormController
(form).
I used Ben Lesh's answer to deal with the same problem. I was displaying a list of notification preferences, and my goal was to hit my api with models that had changed. Not just ng-changed, either; value changed.
I start by initializing some private state on my controller:
// Private state
var originalModels = {};
var changedModels = [];
Then I store copies of the original models retrieved from the API:
// Create a hash of copies of our incoming data, keyed by the unique Code
for (var i = 0; i <= data.length - 1; i++) {
var np = data[i];
originalModels[np.Code] = angular.copy(np);
}
Next, we want to make sure that when a model changes, we add it to a changed models collection (or remove it from the collection if no real change occurred):
function modelChanged(m) {
var originalModel = originalModels[m.Code];
var hasChanged = !angular.equals(originalModel, m);
// If the model has changed and is not present in our collection of changed models, add it
if (hasChanged && changedModels.indexOf(m) === -1) {
changedModels.push(m);
}
// If the model has not changed and is present in our collection of changed models, remove it
if (!hasChanged && changedModels.indexOf(m) > -1) {
var i = changedModels.indexOf(m);
changedModels.splice(i, 1);
}
}
Finally, we tie these together in our ng-repeat:
<tr ng-repeat="np in npc.notificationPreferences">
<td>{{np.DisplayName}}</td>
<td>
<input type="checkbox"
ng-model="np.Sms"
ng-checked="np.Sms"
ng-disabled="!np.SmsEnabled"
ng-change="npc.modelChanged(np)"/>
</td>
</tr>
Now, instead of pushing back every row to my API, I simply push the changed models. This also has a side benefit of being able to ng-disable your submit button if nothing has actually changed (ie the changedModels collection is empty).
lastName
from the ngController of the form, not from the$scope
. – Mik378 Commented Mar 3, 2014 at 16:13name="lastName
" to the field – Mik378 Commented Mar 3, 2014 at 16:21$pristine
/$dirty
are provided byngFormController
but I could be wrong. – glepretre Commented Mar 3, 2014 at 16:31