AngularJS form validation : $dirty value change through ui.router state changes
Asked Answered
T

2

8

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) :

  1. Fill step 1 and go to next section
  2. Check xbox radio and return to step 1 by clicking on the number myMultiStepForm.interests.xbox.$dirty = true
  3. Come back to step 2 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

Tachymetry answered 7/10, 2016 at 14:55 Comment(0)
S
3

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;
  })

})
Swordfish answered 10/10, 2016 at 14:13 Comment(1)
I was looking for something like this... I think the $destroy trick can fix the issue. I try to implement this on my app and will let you know thank youTachymetry
R
2

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:

Explanation for $dirty

$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

  • Suggesting you to replace the $dirty to $pristine

Update 2 :

Why is the value not true when navigating again back

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.

Ropy answered 10/10, 2016 at 11:41 Comment(8)
Can you please explain your changes ? By following the same scenario in my question, I've got myMultiStepForm.interests.$dirty = false at the end. Also I used many <ng-form> to know each field validation and not the global form validation. How would you do without all these <ng-form> ?Tachymetry
You can use multiple ng-form tags to combine controls that may not be located in the same DOM hierarchy. A classic example of this is a submit input on a modal where the button resides in a modal's footer.Deckert
yes. it can be used in the case of combining controls. but in this case I think it is not necessary to have <ng-form> for every control of the form element. Correct me if am wrong!!Ropy
I think you don't understand, my initial issue is : I don't know why the $dirty value doesn't stay true when the user interact with the input and navigate thought the multi step form. The answer's plunkr doesn't solve this issueTachymetry
I clearly understood your question. the problem is 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.Ropy
Ok that's the explanation I was waiting for :) So using ng-form that way is not going to help me solve my issue. Maybe I should not use the $dirty value, but I don't see a simple way without compare the input values from the server each timeTachymetry
Yes. Any other explanation u need?Ropy
No, your explanation is fine now thank you. I just need another approach to solve the issueTachymetry

© 2022 - 2024 — McMap. All rights reserved.