Another if(!$scope.$$phase) $scope.$apply() szenario - need help for clearance - javascript

I have an Angular form with dynamical many subforms. The first form won't get resetted after submit and the subforms will be resetted on every submit.
We can use this whole form more than once, if the user doesn't reload the page.
So after validation and submit, I don't reset the first form, but let the user click it through again and he can add some additional subforms (if nothing changes).
The validation only appears if the user clicks on submit, so the scope variable subFormSubmitted gets true and the required error is still true. e.g.
subForm.salutation.$error.required && subFormSubmitted
On first pageload - everything works fine. When I try to submit the subform, without entering something, the required validation gets shown.
The problem is, after he submitted the form the first time, and he doesn't change anything in the first form and he gets to the dynamical forms the second time, and just click on submit, without entering something, the model doesn't get updated and no validation is shown, although the scope variables has the right value.
The variables
subForm.salutation.$error.required && subFormSubmitted
evaluates to true when I check it in the webdeveloper. However, when I focus an input and type something in, the required validation immediately appears on the other inputs. Also, when I change something in the first form - and then enter the subforms, the validation shows correctly when I press submit.
So I thought, that could be a problem with applying the scope.
What I did after some try and errors, I got a solution that works:
I added
if (!$scope.$$phase) {
$scope.$digest();
}
to the scope function that gets called when I press submit.
This works fine, but after some research, I found out that this is an anti pattern:
Why is using if(!$scope.$$phase) $scope.$apply() an anti-pattern?
So, can someone help me and tell me what's the "right" way to solve this problem?
Basically I controll the visibility of the forms with ng-show=firstFormSubmitted.
$scope.addSubForm = function() {
$scope.firstFormSubmitted = true;
I hope you could understand my problem, sorry for the bad english

$scope.$apply and $scope.$digest are meant be executed in callbacks that run asynchronously (after the initial digest cycle): AJAX requests, setTimeout/setInterval/setImmediate, non-$q promises, DOM event listeners, etc.
It is Angular's job as a framework to do it automatically in all asynchronous built-ins ($http, $timeout, directives), and it handles the job. Well mannered third-party extensions do the same thing.
The reason why !$scope.$$phase && $scope.$digest() can be considered an antipattern is that in well-shaped app the developer always knows if the code is performed on digest or not, thus this check is redundant. And if it's not, this is the indicator of some flaws in application design.

Related

Can I block browser UI thread until Angularjs promise finishes?

I have a timing issue with a form with multiple input boxes. Some input-boxes are disabled by default.
When a user presses the TAB key to focus next input-box, I want a promise to block the UI-thread until it has been resolved/rejected.
This is because the focus-state (enabled/disabled) of the next input-box depends on the result of the http request in the previous input-box.
If the user types fast and presses the TAB-key, focus will be set to an already enabled input-box further down the chain instead of the next one.
The promise is used in a $http request and when the response arrives, logic will decide if the next input-box should be enabled or not.
Every input-box uses ng-blur to detect loss of focus. The directive for the input-box also keeps track of the search-promise and I have access to it in the blur method inside the directive.
Can it be done?
Best I can offer is the standard ng-cloack. Where you will avoid displaying things until they are ready.
https://docs.angularjs.org/api/ng/directive/ngCloak
The other option is you managing "manually" the display of things with things like a spinner or simply by hiding controls until things "are ready".
You should not block the whole UI. A synchronous AJAX request would do what you want, but it has been deprecated.
You could just block (disable/read-only) the input field that needs to be validated while the request is pending. You should show some indication besides the grayed out field so the user understands that there's an action pending.

Is there an event for the browser's back button being pressed?

I am supporting an e-commerce app, which pretty much makes and submits orders.
A user found that if they submit their order, and press back really quickly, they can cause an error condition.
I want to prevent this. When the user clicks submit, I want to bind some kind of event to the browser's back button that instead will redirect them to the Index page. However, after about two hours of Googling (including a few StackOverflow topics), I have not found any clear way of influencing the behavior of the back button.
I briefly attempted to use history.pushState(), but as the HTML 5 documentation mentions, that will not cause a redirect; it merely alters the displayed URL/state.
Similarly, the history.onpopstate event appears unhelpful, because it occurs whenever a state is removed from the history listing; I'm looking for an event that occurs whenever the history listing is traversed backwards.
Question: Does an event for the browser's back button, or at least a way to prevent this particular stupid user trick exist?
You can't listen to the browser back button because it's outside of your reach (it's not part of the DOM).
What you can do is fix the previous page so that it detects if you've used the back button.
Without more information I can't give you any tips on how to achieve that.
Also, an error condition is not necessarily a bad thing. Just make sure it's clear what is happening: the error message should make sense.
Wrong answer...
Instead listen to window.onBeforeUnload and ask the user if he knows what he is doing. Return false if not. This is usually done via a confirm dialogue

Using form submit handler in jQueryMobile for validation, but preventing sending

I'm building an app in jQueryMobile and PhoneGap. I have an approach to validate forms that works nicely while testing in Chrome and on my Nexus S Android phone, but that I'm not sure will work on all devices.
This is the situation:
I have a few simple forms. The data that the user enters is stored locally. So I don't need POST or GET requests (in fact, I want to avoid them like the plague). I could just create a few inputs and a button and add a click handler to the button for everything to work nicely.
Except I really like the HTML5 form validation stuff. And that validation only (mostly) takes place when the submit event is fired on the form. So I have chosen to put the inputs and the button inside a form element, make one button a submit button, and listen for the submit event being fired on the form.
Another approach (shown here for the 'Delete' button) is to simply listen for a click on the button without making any use of the submit process. After all, if a user wants to delete an item, there is no need to validate the form (in my case anyway).
<form id="oneExpenseForm" action="javascript:void(0);" method=''>
<button type="submit" id="submitExpense">Save</button>
<button id="deleteExpense">Delete</button>
</form>
<script>
$('#oneExpenseForm').on('submit',function(){
submitExpense();//This function takes care of everything I want
})
</script>
This seems to work fine. I've also tried setting the form action differently:
<form id="oneExpenseForm" action="submit" method=''>
But that caused the browser to start a GET request, which causes a page reload (which is a really bad and unwanted thing when working with jQueryMobile).
What I'm worried about is that I may be setting things up for failure in certain browsers. After all, some browsers might see this action attribute...
action="javascript:void(0);"
...as a reason to not fire the submit event at all. That would be bad, since I need the submit event to fire for validation and
Am I taking needless risks here? Or do all browsers fire the submit event even when the form action is set to null, void, etc?
One more thing:
I could of course just specify...
action="javascript:submitExpense();"
...in the HTML file. But this is something that I want to avoid, since I am protecting my Js code with obfuscation. That involves changing the names of functions to unreadable code like aR3df(); using a special piece of software. I would prefer not to have to look up the obfuscated names and enter them in the HTML every time I am publishing an update.
EDIT:
After the first answer came in to use e.preventDefault() and that did not work, I figured it might be relevant that I am binding the submit event handler inside another function. That's because jQueryMobile wants you to only do your custom handling stuff after the "page" has been initialized.
In previous jQuery versions it didn't work to use the .live() binder within other functions. But this is not the problem either. Even when I bind the handler the ".live()" way using the new .on() binder...
$(document).on('pageinit','#oneExpensePage',function(event){
$('#oneExpenseForm').on('submit',function(){
submitExpense();//This function takes care of everything I want
})
})
... the page reload (i.e. form submit) persists.
Leave action empty action="" and prevent the event's default action on submit:
<script>
$(function() {
$('#oneExpenseForm').on('submit',function(e){
e.preventDefault();
submitExpense();
});
});
</script>

Change value of text box using javascript (Jquery) before post back ASP.NET

I am trying to change the value of a set of text boxes (that hold greyed out suggestions in them) when the user presses the submit form button. This button runs a server side method using OnClientClick that submits the data and does a whole slew of other things.
Now my problem is that i can't either: fit in a javascript function that will change the values before the server gets hit, OR call the server side method in the javascript instead of the button OnClientClick event.
Ive tried:
$(this.form).submit(function(){
//Change value here
});
//using OnClientClick to call function
and
$("#"+"<%=submitBtn.ClientID %>").click(function(){
//Change values here
__doPostBack("<%=submitBtn.ClientID %>");
});
//not using OnClientClick to call server method
(pretty sure that won't work)
and
$("#"+"<%=submitBtn.ClientID %>").click(function(){
//Change values here
});
//using OnClientClick still to call function
Im stumped
Edit
Right I obviously didn't give enough info,
What happens when I use submit events is the server event fires before the JavaScript event, therefore when do a server side validation before I send the values away, I have the wrong values, there isn't any point in changing to client side validation because i will still have the same problem when I send the form data back to the db.
Update
So i still have a problem (both with this and mentally because of this).
Because of the idiots who worked on this before me (now i have to fix it) they removed the submit behaviour (asp.net) from the button at the bottom, because they use some server side trickery to figure out if some validators should be on or off (when really it should be client side that does that), hence they had to turn it off because it would fire validators if it didn't.
ANYWAY... So I'm still having trouble, the on click function for the button doesn't seem to fire in time or the scripts run simultaneously. I tested this by adding an alert and a breakpoint on the code behind, the breakpoint fires and the alert fires too. sooo..... yeah.
Is there any way i could maybe circumvent this by removing the "onclientclick" from the button and calling the function it calls in the CB?
Any ideas? (Please?)
Small update:
Still can't figure it out :(. Is anyone confused by the question?
Yup, this function should work
$(this.form).submit(function(){
//Change value here
});
But one culprit might be the commented part: "// Change values here." If you're using one of these,
$('#target').text('my new info');
$('#target').html('my new info');
...you will have trouble. You need to use .val()
$('#target').val('my new info');
The form is submitted before JS fires all events.
Have your button's onClientClick event change the values. You can call functions in sequence if you need to.
jQuery does some funky things with events and you can't be sure what order they will fire after they are attached. You must explicitly specify the functions to call.
<button id="some_button" onclick="SetValues(); SubmitForm();" />
Just stumbled across this. It's been a month since you first posted the question so you may have fixed it already. However, thought I'd add my thoughts anyway.
First, it's make sense your code is being ignored. When you bind to the submit and onclick events you functions are added to the list of events handles. Events handlers are processed from the first added to the last added. So the postback is started before your jquery code is called.
To solve this you need to change the code in the function that OnClientClick calls or write a wrapper function that calls your code then calls what OnClientClick called and change OnClientClick to call your wrapper function.
this function should do the trick:
$(this.form).submit(function(){
//Change value here
});
just make sure you enable the text fields again before you send them to the server, otherwise the server won't pick them up
I have used id of submit button to check for a "click"
http://jsfiddle.net/sreeprasad/7urvg/

Javascript event synchronisation

Is there a possible way to synchronize events in javascript?
My situation is following: I have a input form with many fields, each of them has a onchange event registered. there is also a button to open a popup for some other/special things to do in there.
My requirement is, that the onchange event(s) are finished before I can open the popup.
Any ideas how I can achieve that without using setTimeout?
EDIT: further explanation of requirements:
To clarify my situation I try to detail what I'm doing.
I got a form with some input items (order entry matrix form, e.g. article, serial#, count). Every time user changes data in one of the fields an ajax call is triggered by an onchange event to validate the user input and read additional data (e.g. presetting/formating one of the other fields). These ajax calls are heavy and cost time, so I have to avoid duplicate validations.
There is also a button which opens a popup which gives the user an other form to change data he entered before line by line, so it is absolutely necessary that all validations are done before this popup is opened.
At the moment I try to synchronize the onchange events and the popup opening using setTimeout (popup isn't opened before all validations are done), which causes problems at my customers site because these popups are trapped by the popup blocker.
So I need to open my popups without getting stopped by some popup blocker (IE 6/7/8).
Because of my matrix-form I just can't validate all input items before opening the popup, I need to validate only those which have been changed and are not validated yet (should be at most 1).
It sounds like you are doing form validation, with an automatic popup when the form has been fully completed. To do that, you write a single validation function in javascript that checks every field on the form. You can fire this function from each of your OnChange events, and have the function open the popup when the entire form successfully validates.
Consider checking out jQuery, when you have a little free time.
http://jquery.com/
you can set up a little callback to your onchange events to insure that all of your validation occurs before the popup.
function onChange(callback)
{
// Do validation
// Call the callback
callback();
}
function showPopup()
{
// Show the popup
}
Then on your onchange call just call
onChange(showPopup);
If you set a global variable and use setTimeout to check if it is set properly. Depending on how complex the situation is you can either use a boolean, two booleans, a number that increments, or even an object. Personally I would proly use an object as that way I know which one hasn't fired yet. something like var isDone = {username: 0, password: 0, password2: 0};
Let assume by input fields you are meaning only text inputs and not any checkboxes or comboxes( I'm guessing you are trying to make a sort of auto-completion).
My advice is to use onkeyup and onkeydown.
var keypressed = false;
function onkeydown( )
{
keypressed = true;
}
function onkeyup( )
{
keypressed = false;
setTimeout( function()
{
if (!keypressed)
show_popup();
else
setTimeout( this.calee,1000)
}, 1000 );
}
Set flags (variables) for each group of validations.
Initiate the flag at 0.
Set the flag to 1, when
validation is complete for the group.
When the user pops the button, if all
flags are 1, popup the window.
The callback that Jon mentioned would solve the problem of "what do you do if they are not yet all validated?"
EDIT: Added after clarification:
Have you considered adding the popup button, via DOM methods (easy) (or innerHTML, if you like), after everything is validated? That way, there is no option shown before its time. :D
Also, do you test if a popup is blocked? If it is, you could branch to either a notice to the user that their blocker is blocking the editor; or to loading your editor into an iframe automatically; or to loading the editor to the main page via DOM methods (appending documentFragment, etc.).
Some blockers give users the option to block even popups generated from clicking on links (which were traditionally off limits to blockers). I would think you would benefit from some kind of a backup method, or at least a warning system in place regardless.
HTH
i don't think i have completely understood your question, but here are some thoughts on solving problems you may have :)
first, i'd deactivate the popup-opening button when the ajax call is sent. then, when the requested data arrives and all validation is done, activate it again. you can do this with a counter: increment it for every sent request, decrement it as soon data arrives and validation is completed. activate the popup opening button when data arrives and the counter is zero. this prevents the user from clicking the popup opening button while there are still validation requests pending.
you can use the same technique for the input fields themselves: lock the input fields that await validation by setting them to readonly, unlock them when everything is done.
to prevent problems when the user changes form values while the ajax call hasn't yet returned, you have several options:
use a timer for sending the request: everytime an onchange event is fired, wait x seconds before sending the request. if another onchange event happens before the ajax request is sent, reset that timer. this way, several onchange events withing a certain timeframe trigger just 1 ajax request. this helps reducing load.
you can calculate and store checksums for every position, so if an onchange event is fired, calculate the checksums again and compare them. this way you know which parts really have been changed, avoiding unnecessary validation requests.
also, never bet on time (if i understood the settimeout stuff right). x seconds may be enough under normal circumstances, but in the worst case ...
We needed something similar for a wizard where some steps required AJAX validation. The user wouldn't be allowed to close the wizard by clicking Finish if there were any pending validations. For this we simply had a counter for pending validations, and a flag to signal if the user was wishing to close the wizard. The basic algorithm was:
If a new AJAX validation is initiated, increment the "pending" count.
When an AJAX validation returns, decrement the "pending" count.
If, upon decrementing, the pending count reaches zero, check the "finish" flag; if it is set, finish the wizard.
When the user clicks Finish, check the "pending" count; if it's zero, finish the wizard; it it's non-zero, set the "finish" flag.
This way, synchronization can be handled with just two variables ("pending", "finish").
I strongly advise against using multiple flags for each different AJAX operation; a state machine usually gets out of hand when states are tracked with multiple state variables. Try to avoid it unless it's absolutely necessary.
I also don't suggest using setTimeout to arbitrarily wait until desired conditions are met. With the counter approach above, your code will act on changing conditions, as soon as they change.

Categories

Resources