I have configured the dojo combobox as follows:
this.autoComplete = new dijit.form.ComboBox( {
id : this.name + "_term",
name : "search_id",
store : this.dataStore,
searchAttr : "term",
pageSize : "30",
searchDelay:500,
value : this.config.inputText,
hasDownArrow : false
}, this.name + "_term");
The issue here is that when the user enters their search term and hits [Enter] before the 500ms, the service request gets canceled (common when copy and pasting a search term). What I expected to happen is for it to simply ignore the [Enter] event until the request is complete and options are displayed in the dropdown. The user could then hit enter again to submit the first item in the response.
Hoping to get some suggestions on how to handle this scenario. I've looked through the api for dijit.form.ComboBox, but didn't see anything compelling that could address this. Note that the exact same behavior exists if I use FilteringSelect instead of ComboBox. The interesting thing is that FilteringSelect treats this scenario as an error that is handled by the "invalidMessage" param. I don't understand the benefit of treating this as an error.
I've (temporarily) solved the issue by monkey patching dijit.form.ComboBox by overriding the _onKeyPress function. I'm using dojo v1.5 and noticed that v1.6 changed _onKeyPress to _onKey. So upgrading will obviously break things.
I've updated the [Enter] event handling like so:
case dk.ENTER:
// prevent submitting form if user presses enter. Also
// prevent accepting the value if either Next or Previous
// are selected
if(highlighted){
// only stop event on prev/next
if(highlighted == pw.nextButton){
this._nextSearch(1);
dojo.stopEvent(evt);
break;
}else if(highlighted == pw.previousButton){
this._nextSearch(-1);
dojo.stopEvent(evt);
break;
}
}else{
if (!module.autoComplete.item) {
doSearch = true;
}
// Update 'value' (ex: KY) according to currently displayed text
this._setBlurValue(); // set value if needed
this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
}
// default case:
// prevent submit, but allow event to bubble
evt.preventDefault();
// fall through
break;
The code in question is:
if (!module.autoComplete.item) {
doSearch = true;
}
I'm basically telling it to do the search only if the autocomplete object instance exists and hasn't recieved any items yet. This is an ugly hack, but it's solving the issue for the moment. I would still love some suggestions on how to handle this better.
Related
I am using Quill (Rich Text) and need to find a way to checking to see if the text has changed when the page does a form submit. I am quite new to using Quill and have been looking at the events here. Using the text-change triggers everytime the text is changed (obviously) but I have other Form Input controls on the page which are checked on form submit to see if they have changed... I need my RTF boxes to do the same.
EDIT
I have managed to get the Event Firing using the example below. My problem now is that the event appears to trigger even when the editor is pre-populated on page load. I dont want to acknowledge these initial loads, only if the text has been changed by a user.
Two ways to do so:
1) listen for quill changes and if any occurred, raise a flag telling your form content has changed (flow: if you add a char, then delete it, your flag would be true even if resulting content is the same)
Using :
let changes = false
quill.on('text-change', function(delta, oldDelta, source) {
changes = true
})
2) comparing two snapshots of the document to detect front-end if changes occurred. You could either compare strings (with quill.getText()) this is the simplest, but you could miss lot of things, I would recommend to compare objects (with quill.getContents()) and use lodash or other deep equality objects method check.
Using:
const initialContents = quill.getContents()
const beforeSubmitContents = quill.getContents()
const hasChanged = _.isEqual(initialContents.ops, beforeSubmitContents.ops)
for detect if exis change only implement this function
quill.on('text-change', function(delta, oldDelta, source) {
if (source == 'api') {
console.log("An API call triggered this change.");
} else if (source == 'user') {
console.log("A user action triggered this change.");
}
});
this function detect if write or have a change on editor, detect if has change on your words or font or image...etc.. !!
In this case i use the example of official page:
page official
result :
I have a problem concerning the notificationBox. I create a notification using
appendNotification( label , value , image , priority , buttons, eventCallback )
and supply a button in the buttons argument.
Now, I want to prevent the notificationBox from closing when I hit the button. The XUL Documentation states that this can be done by throwing an error in the eventCallback function:
This callback can be used to prevent the notification box from closing on button click. In the callback function just throw an error. (For example: throw new Error('prevent nb close');)
This does not work for me, however, it works when I add the throw-statement to the callback function of the button itself.
Is this a bug in XUL or an inconsistency with the documentation?
Is there any harm done by adding it to the button's callback function?
In my opinion, this is an error in the documentation not a bug in the code. However, throwing an error in your button callback to prevent closure is not the best way to accomplish that goal.
Looking at the source code, there were clearly multiple discrepancies between the code and the documentation regarding how buttons work on a notification.
There is a specifically coded method of preventing the notification closing from within the button callback (return true from the callback).
Throwing an error in order to accomplish a normal functionality is usually a bad programming practice. Doing so also results in an error showing in the console every time your button is pressed. Having errors intentionally showing in the console under normal operation is bad. It also can result in your add-on not being approved in review.
As it was documented (not as operational), if you wanted to close when one button was pressed and not close when another was pressed, you would have to store in a global variable which button callback was last called and then choose based on that information if you wanted to prevent closure when your notificationBox callback was executed. That would be an inappropriately complex way to design operation of these notification buttons.
Given all that, I would say that intentionally throwing an error in order to prevent closure is not the "correct" way to do it. While, trowing an error to prevent closure doesn't cause any harm to the operation of the notification box, it does show the error in the console, which is bad.
The correct way to prevent the notification from closing from within the notification button callback is to return a True value from the callback.
While it is possible that the previously inaccurately documented way of doing this the way they intended to have it operate, it is not the way it actually works. Given
It is easier to update the documentation than it is to make changes to the code.
The code works in a way that is better than the documented method.
There were other inaccuracies in the documentation that would have prevented people from using functionality which was supposedly working (popups/menu buttons).
I have, therefore, updated the documentation to reflect what is actually in the source code and copied, with some modification, the code from this answer to an example there.
Here is some code I used to test this:
function testNotificationBoxWithButtons() {
//Create some common variables if they do not exist.
// This should work from any Firefox context.
// Depending on the context in which the function is being run,
// this could be simplified.
if (typeof window === "undefined") {
//If there is no window defined, get the most recent.
var window=Components.classes["#mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator)
.getMostRecentWindow("navigator:browser");
}
if (typeof gBrowser === "undefined") {
//If there is no gBrowser defined, get it
var gBrowser = window.gBrowser;
}
function testNotificationButton1Callback(theNotification, buttonInfo, eventTarget) {
window.alert("Button 1 pressed");
//Prevent notification from closing:
//throw new Error('prevent nb close');
return true;
};
function testNotificationButton2Callback(theNotification, buttonInfo, eventTarget) {
window.alert("Button 2 pressed");
//Do not prevent notification from closing:
};
function testNotificationCallback(reason) {
window.alert("Reason is: " + reason);
//Supposedly prevent notification from closing:
//throw new Error('prevent nb close');
// Does not work.
};
let notifyBox = gBrowser.getNotificationBox();
let buttons = [];
let button1 = {
isDefault: false,
accessKey: "1",
label: "Button 1",
callback: testNotificationButton1Callback,
type: "", // If a popup, then must be: "menu-button" or "menu".
popup: null
};
buttons.push(button1);
let button2 = {
isDefault: true,
accessKey: "2",
label: "Button 2",
callback: testNotificationButton2Callback,
type: "", // If a popup, then must be: "menu-button" or "menu".
popup: null
};
buttons.push(button2);
//appendNotification( label , value , image (URL) , priority , buttons, eventCallback )
notifyBox.appendNotification("My Notification text", "Test notification unique ID",
"chrome://browser/content/aboutRobots-icon.png",
notifyBox.PRIORITY_INFO_HIGH, buttons,
testNotificationCallback);
}
I have the following code in a javascript file:
if(dojo.byId('WC_selectedColorNumber') == null && this.defaultColor != null)
{
dijit.byId('WC_color_selection').domNode.style.display = 'block';
dojo.html.set(dojo.query(".message__button .add"), "Add product with only base color " + this.defaultColor + "?");
var userResponse = true;
dojo.connect(WC_add_color_yes, "onclick", function(evt){
userResponse = true;
});
dojo.connect(WC_add_color_no, "onclick", function(evt){
userResponse = false;
});
//var userResponse = confirm("Add product with only base color " + this.defaultColor + "?");
//I WANT TO WAIT HERE FOR THE RESPONSE
if(userResponse == false) //if user clicks Cancel or 'no', display a message and leave the function.
{
alert("Remember to select a color before adding to cart."); //should be a tooltip/popup (not javascript alert) with the same message
return; //return so item doesn't get added to cart
}
}
Firstly, the logic behind this code is correct and it works perfectly well when using javascript confirm's.
As of now, everything comes up and displays correctly, and clicking the buttons perform the correct actions (if I put a console.log in the onclick dojo events, they do indeed print to the console when I click the buttons). However, the program doesn't wait for the responses and continues beyond the dojo.connect methods before it sees the user's input.
I need it to wait until either the yes or no button have been pressed, but I cannot figure out how to do it. I've tried using a
while(userResponse == null);
but a) it's generally a terrible idea and b) it didn't work anyways.
How can I make my code wait until the user has clicked one of the two buttons?
If you can make a jsfiddle I'd be able to help you more, I think, but your dojo.connect calls shouldn't be inside a logic flow like this. Instead, set up your connects on widget startup, and have them act generically.
In your example code, it looks to me like saying "Yes" means "Use default color", and "No" means "User must specify color". So...
startup: function () {
this.inherited(arguments);
dojo.connect(WC_add_color_yes, "onclick", dojo.hitch(this, function(evt){
this.useDefaultColor();
}));
dojo.connect(WC_add_color_no, "onclick", dojo.hitch(this, function(evt){
this.displayColorPicker();
}));
}
And then... only display those two buttons (or the dialog they're hopefully in) when applicable.
There is no "wait" or "sleep" function in javascript and each invocation of javascript code executes to completion (it does not get interrupted in mid execution by a response to some other event). You have correcly identified the historical execeptions that overcome this - global alert and confirm functions execute in browser native code and wait on user input.
Because of this your code will have to be restructured in some way, e.g. an event handler for "add to cart" validates the color choice and calls a function to really add it to the cart if valid. If it is not valid it modifies the DOM to present user with some buttons. The handler for the "yes" option would likewise call the same function to really add it to the cart.
Specific code is outside the scope of this answer - there must be many methods in page and code design to achieve the desired result. For example only: breaking up the sequential code and putting it in separate event handlers, coding using Promise objects defined in EC6 but not supported in MSIE, or perhaps even providing an option of "none - base color only" in the color selection logic.
FYI the dojo 1.10 toolkit documentation reports support for Dojo Promises but I leave research to determine its suitability with you.
I'm developing a social network website and I'm working with php/mysql/jquery and I want users to have the most user-friendly website experience. That's why I try to avoid a save (submit) button at all. So, when they're on their own profile and enter information like what languages they speak or where do they live, I want to auto-create a page entry, if it doesn't exists already.
For example if somebody speaks german and that entry isn't available in the database yet (it's called pages with rows title,text,date_added). This is most likely comperable with a Facebook page you can create on Facebook, but this should be about everything (languages, companies, locations, activities).
So my specific problem is now, how to implement this feature the most smartest way. I mean on what event should this save be triggered, because I obviously can't save a new entry on every letter that has been typed. I've already implemented an autocomplete function, so they can choose from entries that already exist, but as mentioned I want them to create new entries (pages), too. Most preferably without "flooding" the database with new entries.
The things I thought of:
When a user switches from one input to another and input has been
made. Can this even be registered by JQuery? But here's the
question, what if he leaves the page without switching to another
input.
Save all data only then if he leaves the current page. I think
there is this onunload function in JQuery.
Get confirmation from the user: "This entry hasn't been made yet.
Do you want to save this entry?". But when should I ask this? I
can't predict when the user is finished with typing.
You could do the following:
1) Every time the user presses a button or clicks inside an input field - register a timeout using setTimeout (if the timeout was already set - clear it). The actual timeout in ms is up to you, it depends on the users and the data they are entering - maybe half a second or a whole second will be fine.
2) Inside the timeout - submit the data to the backend
3) And as you mentioned - if the user attempts to leave the page with a timeout waiting to happen - ask him if he really wants to leave. Keep in mind you are limited in what you can do from the unload handler itself, so it is best just to return him to the page and invoke the save then (as AJAX will probably not work from that handler, or at least it will not be consistent in all browsers).
var to = null;
$(':input').on('click contextmenu keyup blur', function () {
if(to) { clearTimeout(to); }
to = setTimeout(function () {
$.ajax(...send data to server...).done(function () {
// possibly show a message indicating the data was saved
})
}, 1000);
});
window.onbeforeunload = function() {
if(to) {
return "Are you sure you want to navigate away, you have unsaved data?";
}
}
I have an app where you can enter some information about a customer. I have the problem that this information easily can be overwritten by other changes in the app.
I want:
A textarea showing the information
The message shown should update as changes are made to it in other places in the app
If the user has changed the message in the textarea, the message in the textarea should NOT be overwritten by server side changes or other changes in the app
When a save button is pressed, the model is updated
Simply doing this:
<textarea ng-model="customer.info"></textarea>
<button ng-click="save(customer)">Save</button>
won't work, because if something is changed to the customer object on the server, the information field will be reset and the user's changes will be overwritten.
Update:
In response to your comment/updated question: I'd simply track the state of the textarea using a couple of event listeners, that check if the user changed the value. If so: the textarea is no longer kept in sync with the server data.
I also check if the textarea has focus, if that is the case, the value should not be updated, because the user might be editing its contents.
Lastly, I've attached a blur event handler, that checks the value again, in case the user "undoes" changes. If a user adds a char by accident, and removes it later on, the textarea's value might be the same as the last known value that came from the server, in which case, the textarea's value should be kept in-sync again.
I've set up a simple fiddle that uses an interval to do all this. Replace the interval-related code with your worker, ajax callback or whatever it is you're using. If it's a worker, unsetting the interval should be replaced with unbinding the onmessage handlers with a handler function that syncs the textarea or doesn't sync the textarea. Alternatively use a single function that checks the changed flag whenever it wants to change the textarea's value. Plain and simple.
Here's the fiddle
And here's the code (the fiddle version contains some actual comments that explain various bits and pieces):
(function (txt)
{
var initialValue = txt.value,
changed = false,
id,
callback = function()
{
if (changed === false)
initialValue = (txt.value += ' Server-sync');
else
{
clearInterval(id);
id = null;
}
};
txt.addEventListener('change', function()
{
if (this.value !== initialValue)
changed = true;
}, false);
txt.addEventListener('focus', function()
{
changed = true;
}, false);
txt.addEventListener('blur', function()
{
if (this.value !== initialValue)
changed = true;
else
{
changed = false;
id = setInterval(callback, 5000);
}
}, false);
id = setInterval(callback,5000);
}(document.querySelector('#foo')));
Initial answer
Like I said in my comment:
<textarea ng-model="customer.info" id='foo' readonly></textarea>
prevents the value of the textare from being altered by the user, but you can still set/change its value in your JS code:
var changeTxt = (function(txt)
{
return function(addVal)
{
txt.value += addVal;
};
}(document.querySelector('#foo')));
Demo
To keep the value of the textArea in sync, you could perform an AJAX call prior to making any changes to the value client side, or create a worker "thread" that polls the server for changes that have been made since the value was last fetched.
Seeing as you're using angular.js, I must admit that I don't have any experience with how angular can be best used to solve your issue, but a quick google search lead me to 3-way data binding, which does look rather promising.