My use case :
I have a multi step form using ui-router like in the plunkr below. I use ng-form to validate information provided by AngularJS, like $valid, $dirty etc.
After each click on the "Next section" button, I send the form data to the server in order to retrieve it, in case the user quits the form before finishing.
If the user submits the first step twice, I ONLY send the edited data (if the $dirty value is true). All of this is not in the plunkr, I chose to show you a simple form, but my form can contain a hundred fields (radio, checkbox, input, select etc.).
The steps to reproduce the issue (plunkr) :
myMultiStepForm.interests.xbox.$dirty = true
myMultiStepForm.interests.xbox.$dirty = false
Why is $dirty value changed to false? I guess it's because the <ng-form>
is displayed again and all validation data is reset.
Is there a way to avoid this ? Or maybe something other than <ng-form>
to handle validation of subsets of fields ?
This is the plunkr : http://plnkr.co/edit/WclqVgiBvUXlsGdSCcj0?p=preview
When you link a form or any controller inside it, it always starts out as $pristine
. The reason is that the models like formData.type
simply have some values, angular has no way to know that those values were the default state, coming from the server, or are the result of previous user interaction; they are a simple string
or something without this kind of metadata attached.
To achieve what you want, you have to manually track the $dirty
state across state transitions, and apply $setDirty
on the form when needed.
Here is a quick example, adding a controller to the form step pages, which saves the form state on exit (to a shared service instance, you could add this via resolve
too) and restores it at construction. The current formPage
is injected via a default parameter value so that the same controller can be used for all steps.
// router:
$stateProvider.state('form.interests', {
url: '/interests',
controller: 'FormStepController',
params: { formPage: 'interests'}
templateUrl: 'form-interests.html'
})
// state
angular.value("formDirtyState", {});
// controller
angular.controller("FormStepController", function($scope, formDirtyState, $stateParams) {
var formPage = stateParams.formPage;
for(var formField in $scope.myMultiStepForm[formPage]) {
if(formDirtyState[formPage] && formDirtyState[formPage][formField])
$scope.myMultiStepForm[formPage][formField].$setDirty()
}
$scope.$on("$destroy", function() {
for(var formField in $scope.myMultiStepForm[formPage])
formDirtyState[formField] = $scope.myMultiStepForm[formPage][formField].$dirty;
})
})
I solved and the solution is in the below plunker.
Referring to this :
Note: the purpose of ngForm is to group controls, but not to be a replacement for the tag with all of its capabilities (e.g. posting to the server, ...).
as per Angular Documentation for ngForm element.
Also I wonder why you are using many <ng-form>
elements.
This is the only possible solution for your problem.
Plunker Modified
Update 1:
$dirty is set to true only if the user interacted with that particular element in the current scope.
If you're so particular with true/false problem
Update 2 :
When you navigate from one form to another the scope of the ng-form of that particular element is added to the parent controller, when the same ng-form is visited again it overrides with that of the existing one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With