1
votes

I have a parent form containing multiple sub forms (e.g. http://jsfiddle.net/riemersebastian/9zr00ear/3/)

When the user changes any of the fields, the respective sub form and the parent form get the class ng-dirty to indicate something has changed.

I have also a save-button (actually it is a link) on each of the sub form which saves all sub-form changes and calls subform.$setPristine to update the state of the subform.

However, I would like the parent form to also take notice when there are no dirty subforms anymore and thus remove its' ng-dirty class.

I have come up with this solution: http://jsfiddle.net/riemersebastian/9zr00ear/3/

As you can see I manually check in the controller using jQuery if there are any subforms left marked as dirty, and if not I set the parent form pristine. For that I need to delay the jQuery using $timeout otherwise the call of $setPristine might not have yet changed the DOM and thus the parent form would not be updated.

I would like to know, is there a better way of doing this?

Here is the code:

var myApp = angular.module('myApp',[]);

myApp.controller('FirstCtrl', ['$scope', '$timeout', function($scope, $timeout) {          
    $scope.setFormPristine = function(subForm) {
        console.log(subForm);
        subForm.$setPristine();    

        $timeout(function() {
        if (!($('body').find(".subForm").is(".ng-dirty"))) {
           console.log("none is dirty, set main form pristine")
           $scope.form.$setPristine();
        }
        }, 10);
    }    
}]);

angular.bootstrap(document, ['myApp']);

<body>
    <div ng-controller="FirstCtrl">            
        <div>
            (outer) form is dirty? <b>{{ form.$dirty }}       </b>
        </div>
        <form name="form" ng-init="variants = [{duration:10, price:100}, {duration:30, price:200}]">
            <div>
                <div class="subForm" ng-repeat="variant in variants" ng-form="subForm">
                 <div>
                    <label>Duration:</label>                    
                    <input name="duration" ng-model="variant.duration"/>                    
                 </div>                  
                 <div>
                    <label>Price:</label>
                    <input name="price" ng-model="variant.price"/>                  
                </div>
                 <a href="" ng-click="setFormPristine(subForm)">Reset Form to pristine</a>
                 <div>
                    (inner) subform is dirty? <b>{{ subForm.$dirty }}           </b>
                 </div>
                  <br></br>
                </div>
            </div>            
        </form>        
    </div>        
</body>
1

1 Answers

0
votes

ARG! Get that jquery out! ;)

My first solution would be directives and controller api's to have the sub form push notifications to the main form, some pseudo code:

    app.directice('mainForm', function($scope) {
    return {
        restrict: 'E',
        transclude: true,
        controllerAs: 'mainCtrl',
        controller: function($scope){
            this.publishState = function (childIsDirty) {
                if (!$scope.parentForm.$dirty && !childIsDirty)
                    $scope.parentForm.$setPristine();
                if (childIsDirty) {
                    $scope.parentForm.$setDirty();
                }
            }
        },
        templateUrl: 'parentForm.html'
    };
});

app.directice('subForm', function ($scope) {
    return {
        require: '^parentForm',
        restrict: 'A',
        transclude: true,
        controllerAs : 'viewModel',
        link: function (scope, element, attrs, parentForm) {
            console.log("Hey I am subForm!");
            scope.$watch('subForm1.$dirty', function(isDirty, wasDirty) {
                parentForm.publishState(isDirty);
            });
        },
        controller : function() {
            var viewModel = this;
            viewModel.myData = "Hello World";
        },
        templateUrl: 'subForm.html'
    };
});

With the html you should steer clear of nesting the forms, considered bad html form (sorry, had to do it), they really don't need to be anyways since the required command will just look at all the other controllers that have been injected.

<div>
    <parent-form></parent-form>
    <script type="text/ng-template" id="parentForm.html">
        <form name="parentForm">
            content
        </form>
        <form name="subForm1" sub-form></form>
    </script>
    <script type="text/ng-template" id="subForm.html">
        <div>
            <input type="text" name="myInput" ng-model="viewModel.myData" >
        </div>
    </script>
</div>

You can also pass in the main form method into the scope '&' of the sub form if you dont want to link.

The other way is to create a shared service/factory that you use to pubsub the changes and then the form controllers would watch the properties or registering their methods to the singleton factory.