I am writing directive, that will act like this:
allow editing of some text (using content editable)
on loosing focus it should save its value to model (lately watched and saved to DB)
there should be button "Undo" which revert changes.
My implementation is: http://plnkr.co/edit/DsWEYQV4j51i4GO6KjSe?p=preview
The only problem I have is when I press "undo" button, DIV lose focus (so 'focusout' event is fired) and value is saved in model, so "undo" button can't revert its value.
( I click "undo" -> focusout event (autosave) -> click event (??? can't revert) )
Possible workarounds I see:
set timeout on blur and cancel it if 'undo' button was pressed. But it is ugly as user can enter value and navigate to other part of app, so timeouted saving won't run any $watch listeners.
save value on focusin and restore it when 'undo' button is saved. This cause another problem: $watch listeners would run with changed value and then run again with previous value (so there would be 2 writes to DB instead of one)
Do anybody have solution for such behaviour (autosave on blur + undobutton)?
How about using underscore.js debounce function or similar to cause a delay on autosave, where it will check for a undo flag and cancel? Not sure what the $watch listeners are doing. Of course it will still not work if the user completely goes out of the app or refreshes the page etc.
Related
I need to allow or stop an event from happening, based on a given condition such as a pop up box with options YES, NO and CANCEL.
I need to notify the user saying that there are unsaved data and if the user wishes to SAVE it, IGNORE it or CANCEL his current action(event such as Selection-change or Click).
I tried to use createInterceptor() function. But could not achieve the functionality.
Can anybody give me some suggestions with example? Basically I want to know how to stop an event.
Thanks..
Edited
I like the idea of using beforeXXX events. But I am still facing problems. As I mentioned, I need to ask the user if he wishes to save the unsaved data, which is a popup message box (With options YES, NO and CANCEL) that runs asynchronously. So by the time I get a reply, the event will have happened.
For ex. lets imagine a situation where, there is a page that displays a list of records in a grid with a pagination toolbar attached to it on the bottom(with a page size of 10 and total number pages is 10. So totally 100 records) on the left hand side. If you select a record in the list, the details are shown in a detail view on the right hand side.
Now,
I select third record in the list and make some changes to it in the detail view(form).
Without saving the record, I click on next page button on the Pagination toolbar.
It will show a confirm box from the beforeXXX event of Pagination toolbar, but the event will have happened anyway.
Here if the user clicks on CANCEL, I will have to restore the previous state which is already gone. Somehow I will have to go back and select the third record in the list of previous page.
So in order to resolve this problem, if I return false from my beforeXXX event, the next XXX event will be not be triggered.
But if the user clicks on YES or NO options I will have to trigger the event XXX manually which I am unable to do it for a selection-change event as of now.
Like this there can be many operations like list-filtering, searching, Ordering(A-Z/ Z-A), logout etc. For each of this operation I will have to write customised code which totally spoils the readability of the code.
So I was thinking if there is way to, somehow manually trigger the event XXX by holding the event object in beforeXXX...or is there any other way to restore the previous state.
Please give me suggestions....Thanks...
Many events have their "before-" counterpart, for example "beforeactivate". If you return false from this kind of the processing stops.
If not, or if your event does not have it's before- part, then you can use event object passed to all event handlers and call:
ev.stopEvent();
ev.stopPropagation();
return false;
at the end of your handler.
I'm using Backbone in an app & running into an situation where events are not firing.
My View, maps to a dialog interface containing several controls (a text area, a couple radio buttons, etc.) and a save button. I'm defining the following events:
events: {
'input': 'onchange',
'change': 'onchange',
'click .save': 'save',
'click .cancel': 'cancel',
},
Each time any form element in the dialog changes, I fire onchange, which performs some validation and either enables or disables the save button.
In the 'normal' flow, everything works properly. I fill out the textarea, click an option on the radio button, and then click save. My work is saved properly.
In an alternative flow, I try to click save while my cursor is still in the text area, and nothing happens. Then, if I click on the save button a second time, the form saves correctly. I've tried setting a breakpoint at the very beginning of the save method, and confirmed that it is not called during the first button click, and is then called correctly with the second button click.
I've tried to make a minimal example as a jsFiddle to demonstrate the issue, but I can't isolate the buggy behavior.
What could be preventing the click event and save function the first time I click the button? Do you have any tips or recommendations for helping debug an issue like this?
Update: Using a Javascript Profiler
I went back and used a profiler to dig in on what's happening. Here was my process:
Set up page for test, with everything filled in except the primary textarea
Start profiler
Type a letter into the textarea and wait for onchange handlers to fire, which is evidenced by Save button becoming enabled
Wait several seconds
Click Save - nothing happens
Stop profiler
And here's my profile:
What's interesting is that there's a clear flurry of activity when the onchange handlers fire, seen as a visible spike in the flame chart. However, when I click save the first time, there is no javascript execution captured at all! Very curious...
I am working on a windows store application and I want to be able to drag between buttons so that the originally pressed button becomes deactivated and the newly "dragged onto" button becomes activated but I can't seem to get this to work.
I have 2 Buttons inside a StackPanel and the events I have on them are:
PointerPressed
PointerEntered
PointerReleased
PointerExited
PointerCanceled
PointerCaptureLost
PointerPressed and PointerEntered share the same event handler and the rest (the "deactivation" events) share the same event handler.
If I press one button my "activated" event handler is triggered and if I drag off it my "deactivated" event handler is triggered but if I then drag onto the second button the "activated" event handler isn't triggered again.
Strangely, if I start by dragging from off the StackPanel onto one of the buttons the "activated" event handler is triggered. I assume that it is something to do with the internal pointer management stuff but can't seem to find a workaround.
Does anyone know why this is happening and how I can get it to work how I want?
Thanks for your time.
Edit
Okay I've been researching some stuff and I've come across CapturePointer() and ReleasePointerCapture() but this seems to be broken - If I capture the pointer, when I take my finger off the screen, PointerReleased doesn't even get hit.
I've also realized why the "dragging from off the SP onto one of the buttons causes it to 'activate'" - this is because when a button is pressed it doesn't route its event but fires a Click event - meaning the same pointer cannot fire a PointerEntered event of another button, but if it starts outside a Button it will trigger PointerEntered.
This doesn't get me much further but it is a little extra info :)
The concept of Button is a bit unique in regard to mouse capture and how dragging away from it happens. In your scenario I'm not sure if the event model around Button will work correctly for you. On Button, when a pointer is depressed (mouse) it has capture until it is released. This is not the same for touch where a press and drag away is different because in touch there isn't any explicit capture unless you create it.
So what you are hitting is going to be a slight conflict between mouse/touch interactions anyway using Button -- using some other UI element (not sure if you have a styled button) should get you what you want.
I have a top-level menu item which is responsible for refreshing a datagrid in the same window. My current control flow is:
User clicks on refresh
In the click event handler, I:
Disable the menuitem, by setting oMenuItem.IsEnabled = false.
Dispatch an action to refresh the grid and in that action, I re-enable the menuitem, by setting IsEnabled = true
The problem is that the user can click refresh even when it's disabled and it's as if the clicks get queued up. When the action returns it goes on to process the remaining, "queued-up" clicks. What I expect is: all clicks while the menuitem is disabled are ignored and only when it's enabled, the clicks are acknowledged.
The weird thing is that if I just disable it and never enable it it stays that way, i.e., it is disabled.wpf,
"Dispatch an action" you mean by calling Dispatcher.BeginInvoke() or some other kind of async opetation?
Anyway, in both cases you can get a "handle" to the operation (DispatcherOperation or IAsyncResult) and store it as a field when you dispatch your operation. When it completes - set this field to null.
In the click event handler of the menu-item check this field. If it's null it means it is safe to start the operation. If it is not null - return immediately and do nothing.
And something not related to your question but important - why not use Commands? That way you don't need to play with event handling and enabling/disabling. And of course commands can be invoked by multiple means (for example - the user selected the command from the menu using the keyboard and pressed Enter. No mouse clicks involved, but should do the same as clicking the menu item).
Alex.
I have a button which launches a "modal dialog" - it just creates a transparent grid covering everything, with the "dialog" created on top of that.
However I have a strange issue - if I double/triple click the button really fast (or add some delay in the event code), the button click event is executed multiple times, creating multiple overlapping modal dialogs. If the first action in my event is to disable the button (IsEnabled=false) it seems to prevent this.
My guess is that Silverlight is being multithreaded with input - it is not only recording the second click in another thread (while the button's click event is running), but it is jumping the gun by evaluating which control should be the target before the previous event has finished executing. Even though that event alters what control is at those mouse coordinates, it doesn't matter.
Does anyone know anything about this behavoir, or a way around it? If I have something like a save window, where the user clicks a save button, a blocking grid ("Saving...") is placed up while it saves, and then the whole "window" is closed, I'd like to avoid the user being able to queue up multiple save event clicks (this could lead to unpredictable program behavoir).
If you've ever worked with WinForms or WPF, this is expected behavior. Your button is broadcasting its Click event until your modal dialog covers it up. Unfortunately, there is some amount of time between your first click and when the modal dialog covers the button which allows multiple clicks to the original button.
You have two solution choices:
Disable the button after the first click and then re-enable after the modal dialog returns. You've already mentioned that this works.
Write code in the Event Handler of the button to determine if a modal dialog is already being displayed. This way, you're putting the responsibility in one location rather than splitting it up (disabling and re-enabling the button). This would be my preferred solution.
I think what you're seeing is the behaviour of Silverlight's routed events.
You can set the Handled property of the event arguments to true to prevent the event from bubbling.