Mad-D
Mad-D

Reputation: 4669

check for unsaved form data in angularjs ( multiple forms using ui-router )

Implementation Details:

I'm using ui-router to load form page in ui-view div. I referred great example by Andres Ekdahi, How can i do $dirty check on multiple forms using same directive ?

form1

 <form name="myForm" ng-controller="Controller" confirm-on-exit>

form2

<form name="iForm" ng-controller="Controller" confirm-on-exit ng-model="myModel">

app.js ( directive )

myApp.directive('confirmOnExit', function() {
        return {
            link: function($scope, elem, attrs) {
                // condition when back page is pressed 
                window.onbeforeunload = function(){
                    if ($scope.myForm.$dirty) {
                        return "The formI is dirty, do you want to stay on the page?";
                    }
                }
                // condition when user try to load other form (via icons )
                $scope.$on('$stateChangeStart', function(event, next, current) {
                    if ($scope.myForm.$dirty) {
                        if(!confirm("myForm. Do you want to continue ?")) {
                            event.preventDefault();
                        }
                    }

                    if ($scope.iForm.$dirty) {
                        if(!confirm("iform. Do you want to continue ?")) {
                            event.preventDefault();
                        }
                    }
                });
            }
        };
    });

Error:

First time on page load, the $dirty value is false. and I fill the form details and click third icon (file) and i get error for second form dirty check if ($scope.iForm.$dirty) and for $dirty in alert.

angular.js:12520 TypeError: Cannot read property '$dirty' of undefined

and

<form name="iForm" ng-controller="Controller" confirm-on-exit="" ng-model="myModel" class="ng-pristine ng-untouched ng-valid ng-scope">

Demo : Plunker

Upvotes: 1

Views: 1894

Answers (3)

JC Ford
JC Ford

Reputation: 7066

There's an easier way.

Just require your directive be placed on a form element and access the form controller through the link function:

myApp.directive('confirmOnExit', function() {
        return {
            require: 'form',
            link: function($scope, elem, attrs, formController) {
                // condition when back page is pressed 
                window.onbeforeunload = function(){
                    if (formController.$dirty) {
                        return "The form '" + formController.$name + "' is dirty, do you want to stay on the page?";
                    }
                }
                // condition when user try to load other form (via icons )
                $scope.$on('$stateChangeStart', function(event, next, current) {
                    if (formController.$dirty) {
                        if(!confirm(formController.$name + ". Do you want to continue ?")) {
                            event.preventDefault();
                        }
                    }
                });
            }
        };
    });

Upvotes: 2

Pankaj Parkar
Pankaj Parkar

Reputation: 136154

Make you directive more simpler by taking it from name attribute of form, so that only one condition will lie inside a directive & it can be reusable in many places.

<form name="myForm" ng-controller="Controller" confirm-on-exit>

Code

var form = $scope[attrs.name]; //which will take out form name & do search that inside scope
if (form.$dirty) { //form object would always exist, to make sure you could also add one more check `form &&`
    if(!confirm("myForm. Do you want to continue ?")) {
        event.preventDefault();
    }
}

Demo here

Upvotes: 1

holtc
holtc

Reputation: 1820

check if the form exists before you check if it is dirty. Do something like

if ($scope.myForm && $scope.myForm.$dirty) {
    if(!confirm("myForm. Do you want to continue ?")) {
        event.preventDefault();
    }
}

if ($scope.iForm && $scope.iForm.$dirty) {
    if(!confirm("iform. Do you want to continue ?")) {
        event.preventDefault();
    }
}

Upvotes: 1

Related Questions