I have a lengthy form customers will need to fill out. If they click a link on a page, it will navigate away from that Controller and they will lose any data they may have already input.
If I can determine the form has not yet been saved, how can I intercept any click to the links on the page so I can ask the user if they want to save their form first?
No code yet- sorry. Many thanks.
I've written an angularjs directive that you can apply to any form that will automatically watch for changes and message the user if they reload the page or navigate away. #see https://github.com/facultymatt/angular-unsavedChanges
Hopefully you find this directive useful!
sorry for the late answer but mabye someone stumbles upon this and finds it useful. I have encountered the same problem and at the beginning i tryed to use the ng-dirty class applyed to the form element but because i had some custom controls the ng-bind won't be applyed when i changed some fields.
The best way i found so far is to detect when the model is changed with the use of $locationChangeStart event.
$scope.$on('$locationChangeStart', function (event, next, current) {
//we are about to leave the page so it's time to see if the form was modified by the user
if (!$scope.isFormClean())
{
event.preventDefault();
}
});
Related
How to detect unsaved page changes check while moving another page or tab in angularjs 1.5 application.
One of approach is using directives however how to pass related form named to the directive instead of using hard coded solution?
I tried using the service approach as mentioned below but my nested view controller is not able to access the form name. Iam getting $scope.myForm as undefined.
You can handle page change with the event $locationChangeStart for ng-route or $stateChangeStart for ui-router (perform the logic you want inside):
$scope.$on('$locationChangeStart', function(event) {
if ($scope.myForm.$invalid) {
event.preventDefault();
}
});
To tab change etc, you can disable your tab with something like or watever approach you prefer
ng-disabled="!myForm.$valid"
EDIT
You may look at this post to use a service/factory approach :
https://stackoverflow.com/a/25459689/5138917
The module below seem to work for me
https://github.com/facultymatt/angular-unsavedChanges
I have a form where my intent is for required fields to not always be enforced. For example if the user is saving the document as a draft they can enter as little information as they like, if they try and publish the document then they have to enter all the required fields. I'm using a boolean on the controller which changes according to which button has been pressed e.g.
<input type="text" ng-model="field2" ng-required="enforceRequired" />
The problem is that the fields are not re-evaluated when the boolean changes so the form is submitted and then it becomes invalid. Please see this JSFiddle to see what I mean. If you fill in field1 and then click publish it will succeed on the first click and THEN become invalid.
How can I force the validation to run before the form is submitted?
Yarons is right, you are changing the value too late, by late I mean after the form validations has been run. What you can do as a workaround is, after changing the required value, let angular do another cycle and then show your alert. This can be done via $timeout service, although I must mention that it is usually not a good practise to change the flow of your digest actions. It gets pretty messy pretty soon.
Change your publish function like this (and don't forget to inject $timeout)
$scope.publish = function () {
$scope.enforceRequired = true;
$timeout(function () {
if ($scope.form.$valid) {
alert("Published!");
}
});
};
Working fiddle: http://jsfiddle.net/bh9q00Le/14/
The problem is that you are changing the value of enforceRequired in the middle of the digest loop, so the watchers are not re-rendered before you check the input fields' validity (read about digest here).
If you want to get around it, I suggest one of the following methods:
change the value of enforceRequired before you call saveDraft or publish. see example.
call $scope.$apply() after you change the value of enforceRequired. see another example.
I have an ionic app that has a search template that has a form where you can query posts by keyword. I also have a service that returns the post(s) as a list on another view. All that is working well.
My search controller upon submitting the search form uses:
$state.go('app.search.results', {'searchId': hash});
so that I can have a unique url for that search. I needed this unique url to implement 'back' functionality so that if a user clicks on one of the posts in the list, after viewing the post if they decide to click back, they would get to see the results of the search still (by default they would be returned to the search form without any results anymore).
To allow for a back to search results I implemented a custom back button function and put it on the ionic back button element like this:
<ion-nav-back-button ng-click="goBack()">
and then setup a the custom function:
$scope.goBack = function() {
$window.history.back();
}
All of this works well, I can go back to search results and see them, essentially very much like normal browser back functionality.
Problem for me is that when I have gone all the way 'back' via the back button, my initial state contains the 'Back' button and clicking it does not go anywhere and the 'Back' button still shows. Ionic does pretty good about hiding the back button when it shouldn't be there but in this case not so. Any ideas for how to check when history is exhausted and hiding the back button conditionally would be appreciated.
EDIT:
Here is a jsFiddle ; Note: open fiddle in a new, separate tab to see back button issue. FYI Search is in the menu.
One of the few qualms I have with Ionic is their "smart" navigation. I have run into a lot of problems with this myself. My solution was to create a back button of my own, this back button stores previous states and their params so you can go back and not lose search results for example with your scenario.
This component gives you both a back button and breadcrumbs to use (or you can just use back button)
It is open source and feel free to use it!
jscBreadcrumbs
Strange Milk - Breadcrumbs Post
Here is your jsFiddle with the jscBreadcrumbs implemented and working:
jsFiddle
jscbreadcrumbs
You use $window.history.back(), I think you should use $ionicHistory.goBack(); instead. It can control the history and view and state in the ionic way.
I am having a lot of trouble with this and I have finally decided to come here. I feel as if I am making a noob mistake. I created a Passbook pass and I am using PKAddPassesViewController to add the pass. When I present the pass controller, the pass shows up correctly. However, when I decide to press "add" nothing happens, nothing gets logged or anything. After investigating, I added a delegate and the delegate method is as follows:
-(void)addPassesViewControllerDidFinish:(PKAddPassesViewController *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
However, instead for the normal animated dismiss, The controller dismisses as of the app crashes but instead goes to the previous view controller. This is absolutely driving me nuts and any help at all would be greatly appreciated :)
Thanks.
The addPassesViewControllerDidFinish is an optional delegate method, and it is called after the PKAddPassesViewController view controller has been dismissed. In your case, your code could be crashing because you are attempting to dismiss the parent view controller (self).
When 'nothing happens' when adding a pass, it is usually because the pass is not valid. The pass signature does not get checked until after the 'Add' button has been pressed, so this may explain why you see a pass displayed, but then it disappears after you press add. If everything works as expected if you press cancel, then this is probably your issue.
To get more info on what is happening to the Pass, turn on 'Additional Logging' in the Developer Settings on your device, then check the console log of the device (from the Organizer) as you try to add the pass to see if it gives any clues as to why the add is failing.
I came on this site to research a problem that I've been having regarding negating Backbone.js's history for certain elements. Thankfully, I came across this answer: here.
While it had provided a solution for me, the ultimate result was partial.
PROBLEM:
I am using Backbone.js for my navigation. I navigate to a route called "Portfolio" and then a number of images show up which can then be clicked again to view information. In order to render this information, I have it going through another route. The aforementioned resolution had me assigning the following code to negate these Portfolio links from being part of the History:
$(this).bind('click', function(e){
theSiteController.navigate('portfolio/portfolioSelection/' + i, {trigger:true, replace: true });
});
I had applied replace: false to the .navigate and it worked: whenever I hit the Back button, I would skip back to the origin page that I came from. HOWEVER, when I hit the Forward button in my browser, it points me back to the Portfolio link that I had clicked before, which looks like this:
#portfolio/portfolioSelection/4
I have it set to a different View, so I'm wondering if that has something to do with it.
QUESTION:
When I click the Forward button in my browser, how would I re-route it so that way it goes to
#portfolio
instead of
#portfolio/portfolioSelection/4
To view the site, please click here.
I apologize if this has been answered, but I can't seem to find a proper resolution.
Thank you, in advance, for your help!