I've got a problem with switching between element classes - probably sth stupid, but I couldn't find the answer.
In my system I display a list of items. Now I want to be able to promote items, so that they appear at the top of the list. I created some backend infrastructure which works ok and added things to my frontend: a star (a span with star bg) next to every item's title and a jQuery script which is supposed to:
listen to 'click' event - when I click on a star
get some data- attributes from the span
post them to my controller
the controller checks if I'm allowed to promote items and replies 'true' or 'false'
if 'true' then I switch between 'gold-star' and 'silver-star' classes of the item
For some reason the classes don't switch - only when I refresh the page I can see the effect. I tried debugging with Firebug - it gets to the toggle line, but then nothing happens.
Here's the code:
<span class="silver-star promote">
$(".promote").bind("click", function() {
var $this = $(this);
var itemId = $this.attr("data-id"),
isPromoted = true;
if ($this.hasClass("gold-star")) {
isPromoted = false;
}
$.post('/promoteitems', { itemId: itemId, isPromoted: isPromoted }, function(allowPromotion) {
if (allowPromotion == true) {
$this.toggleClass("silver-star").toggleClass("gold-star");
}
});
});
Thanks in advance!
When you are getting a response back it might not recognise it as a boolean simple test would be to check response as string
From your comment on the question:
...the allowPromotion value is 'True' (with a capital T)...
That tell us it's a string, not a boolean. You don't want to just do if (allowPromotion), because that will toggle the classes even if you get back "False".
Instead:
if (allowPromotion == "True") { // Note the quotes and capital T
// ...toggle the classes
}
Or if you want to allow for possibly getting back something with a lower-case T at some point in the future:
if (/^\s*true\s*$/i.test(allowPromotion)) {
// ...toggle the classes
}
That's over-engineering it a bit (it'll work with "True", "true", " True " [note the spaces], and even an actual boolean true)...
Related
I want to create a toggle function for my localStorage as I am trying to install a dark mode on my site and I want it to remember the user's choice.
I want it load the current preference on page load, if one has been made, and then toggle the state on click. My code is below - however I am clearly making some error on my attempt to toggle the localStorage state.
// ON PAGE LOAD, IF USER PREFERS DARK, ADD DARK CLASS TO HTML
if (window.localStorage.getItem('preferDark')) {
$('html').toggleClass('dark');
}
// TOGGLE STATE ON CLICK
$('.wrapper').click(function(){
if (window.localStorage.getItem('preferDark', true)) {
window.localStorage.setItem('preferDark', false);
} else {
window.localStorage.setItem('preferDark', true);
}
$('html').toggleClass('dark');
});
I know there are various localStorage toggle questions out there but I can't find one quite the same as mine.
You can't keep boolean in localStorage. That's why you have an error.
Try to keep, for example, 0 for false and 1 for true. But remember that localStorage also can't keep integer - you can pass integer or boolean but it will be converted to string.
Try this:
if (+window.localStorage.getItem("preferDark")) {
$("html").toggleClass("dark");
}
$(".wrapper").click(function () {
if (+window.localStorage.getItem("preferDark")) {
window.localStorage.setItem("preferDark", 0);
} else {
window.localStorage.setItem("preferDark", 1);
}
$("html").toggleClass("dark");
});
Pay attention: I use + sign before getting value from localStorage to convert it to Number
I have this problem with MessageBox component in ExtJS 3.4 and I'm searching desperately for a solution.
function supprimerCatalogPreEnregFunction() {
Ext.Msg.show({
msg: document.getElementById('confirmDeleteMessage').value,
buttons: Ext.Msg.YESNO,
icon: Ext.MessageBox.ERROR,
fn : function (btn) {
if (btn == 'yes') {
document.getElementById('deleteForm:deleteCatalogPreEnreg').onclick();
}
}
});
}
The problem is that my btn return value 1, not 'yes' or 'no' as i expect. And it drives me crazy because I've trying a lot of solutions and I can't understand why this happens.
This function is a handler for a new Ext.Button.
The button is part of a new Ext.Panel, buttons:[...].
I can't understand why the button has that strange value and it frustrates me a lot.
Can a missing comma produce this behaviour? Although, I didn't found a missing comma.
L.E:
I've researched more and I looked more carefully in my code and, helped by debugger, I found that my function
function (btn){}
receives as arguments on position 0: value 1, on second position receives Window (my current location) and the third argument is the one wich should've been received by my component, and it looks like ["yes", "", Object{}] etc.
And i think this is the argument I need, where 0st position is the value of my btn, but I don't know where the other arguments come from to know what I need to do to in order to make it work.
As I'm still a little confused, I'll update this with a general explanation about how I've implemented this handler.
So, at Ext.onReady I load a function init()
Then, in this function i made a var deleteButton = new Ext.Button
This button has a handler which is my initial function from question
The deleteButton is added to a new Ext.Panel with buttons:[deleteButton, etc]
This panel is added as item to a Ext.TabPanel
And, finally, TabPanel is added to a ViewPort.
The Sencha Documentation shows the implementation like:
// Prompt for user data and process the result using a callback:
Ext.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
if (btn == 'ok'){
// process text value and close...
}
});
Running the following code using ExtJs 3.4:
Ext.onReady(function(){
Ext.BLANK_IMAGE_URL = '/js/ext-3.4.0/resources/images/default/s.gif';
Ext.Msg.show({
msg: "test",
buttons: Ext.Msg.YESNO,
icon: Ext.MessageBox.ERROR,
fn : function (btn, text) {
if (btn == 'yes') {
console.log(btn, text);
}
}
});
});
Output (Firebug console):
yes (an empty string)
So your code should be working. I would check to see if there are any global overrides being loaded and test it in isolation
Looking at the source it seems that the first param is the button clicked (Ext.Msg.YESNO which may give you a truthy result) but the second parameter passed is the text value you are looking for:
var handleButton = function(button){
buttons[button].blur();
if(dlg.isVisible()){
dlg.hide();
handleHide();
//here the arguments are button, activeTextEl.dom.value and opt... so look at the second argument in your callback
Ext.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value, opt], 1);
}
};
I'd try changing your code to:
function (btn, btnTxt) {
if (btnTxt == 'yes') {
document.getElementById('deleteForm:deleteCatalogPreEnreg').onclick();
}
}
First of all, thank you very much for replies.
It seemed that the problem was from a script that I was included it in my jsf page.
And that messed up parts of my code and that's why my component had a strange bahviour.
I solved it by removing that script and let the function as I posted it initially.
I have a form containing many dijit.form elements and also a dijit.Editor. The form is initially filled with data that I get from the server. The user can change the content and then submit this back to the serer. A classic use case.
When the user submits the form I need to only send the changed data. The problem with the dijit.Editor is that it sometimes changes the initial content even if the user did not make any changes. For example:
The initial content entered in the dijit.editor is this:
"Gesegmenteerde rand, 115 mm~Max 13280 U/min, 80 m/sec</br>~Drooggebruik"
And when retrieving the content like this editorObj.get('value'); it returns this:
"Gesegmenteerde rand, 115 mm~Max 13280 U/min, 80 m/sec<br />~Drooggebruik"
As you can see the </br> is changed to <br />. I know the original value is wrong, but that's because the source sucks and that is out of my control.
So my question is: is there an easy way to check if the content has indeed been changed by the user instead of just by dijit.Editor itself.
Something like this (on the editor markup) works:
onkeypress="MyObject.setDirty(true);"
And just keep track of it on MyObject.isDirty.
This does have the drawback that if the user types into the editor and then modifies everything to be exactly as it was originally, the value will be wrong (ie, true, whereas content is back to original), but it is sufficient for most purposes.
Well if you just want to check IF the user modified something you could try that :
var editorIsDirty = false;
var someConnect = dojo.connect(myEditor, "onChange", this, function(newValue){
if(originalSource != newValue){
editorIsDirty = true;
return;
}
editorIsDirty = false;
});
something like that, you get the idea ;)
What I ended up doing is was adding two extra attributes to the Editor:
dijitObj.set('originalValue', value);
dijitObj.set('value', value);
dijitObj.set('uneditedValue', dijitObj.get('value'));
In the read out of the value of the the editor I use these to determine if something changed at all:
var value = dijitObj.get('value');
if (dijitObj.get('uneditedValue') === value) {
// The value hasn't changed, so we send the original value
value = dijitObj.get('originalValue');
}
When I filter a combobox by adding a filter to the underlying store, sometimes the filter works (items are removed) and sometimes it has no effect. I have debugged the filterBy function; it is being called and is returning true/false as I wish to filter/show items.
I read on the ExtJS forums that the, "Combobox uses filtering (even with triggerAction:'all'), so your own trigger gets replaced by the one from the combobox." Two filters?
What is the proper technique to remove temporarily items in an Ext JS combobox?
Use lastQuery: '' in the config.
I faced a similar issue where the combo box would show all the items when the trigger is clicked the first time, irrespective of the filter.
To make sure the filter in the store is not cleared the first time the ComboBox trigger is used, configure the combo with lastQuery=''
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.form.field.ComboBox-property-lastQuery
You want to understand how to reproduce the behaviour of triggerAction:'all', so why not diving into the code ?
Here the source code of the Class ComboBox :
http://docs.sencha.com/ext-js/4-0/source/ComboBox.html#Ext-form-field-ComboBox-cfg-triggerAction
If you look at the code, you'll see that:
1) When trigger is clicked, method doQuery is called.
onTriggerClick: function() {
var me = this;
if (!me.readOnly && !me.disabled) {
if (me.isExpanded) {
me.collapse();
} else {
me.onFocus({});
if (me.triggerAction === 'all') {
me.doQuery(me.allQuery, true);
} else {
me.doQuery(me.getRawValue(), false, true);
}
}
me.inputEl.focus();
}
},
2) In method doQuery, the interesting piece of code is:
if (isLocalMode) {
// forceAll means no filtering - show whole dataset.
if (forceAll) {
store.clearFilter();
} else {
// Clear filter, but supress event so that the BoundList is not immediately updated.
store.clearFilter(true);
store.filter(me.displayField, queryString);
}
}
3) We can see that the method filter of the Store is called.
You have your answer, the proper technique to remove temporarily items in an ExtJS combobox (in a store generally), is using the method filter on the store.
http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.Store-method-filter
Remember, Your best friend is always documentation!
http://docs.sencha.com/ext-js/4-0/#
You'll need to delete the property 'lastQuery' of the Combobox,
everytime you filter the Store. This is where the ComboBox caches
it's Entryset after it build it up the first time.
So doing something like this:
'window combobox[name=countryselection]' : {
'change' : function(view, newValue){
with(Ext.data.StoreManager.lookup('Subcountries')){
var combobox = Ext.getCmp('MainWindow').query('combobox[name=subcountryselection]')[0];
//force the combobox the rebuild its entryset
delete combobox.lastQuery;
clearFilter();
filter('countryId', newValue);
}
}
}
It works great for me :-)
Keep in mind that filtering does not "recreate" the store with the new data, such that if you filtered a combo with the following values for "apple":
orange
banana
apple
And you clicked on the trigger, "apple" would be shown. However, if you started typing (and have typeAhead: true active, the filtering based on your input will default back to the orange/banana/apple Store.
I use Ext.form.ComboBox in very similar way as in this example:
http://extjs.com/deploy/dev/examples/form/forum-search.html
What annoys me is that when the ajax call is in progress it shows loading text and I cannot see any results from before.
Eg I input 'test' -> it shows result -> I add 'e' (search string is 'teste') -> result dissapear and loading text is shown, so for a second I cannot see any result and think about if it's not what I'm searching for...
How can I change this to simply not to say anything when 'loading'...
The solution is to override 'onBeforeLoad' method of Ext.form.ComboBox:
Ext.override(Ext.form.ComboBox,
{ onBeforeLoad:
function() {this.selectedIndex = -1;}
});
Please be warned, that this overrides the class method, so all of the ComboBox instances will not have the LoadingText showing. In case you would like to override only one instance - please use plugins (in quite similar way).
You may also look at Ext.LoadingMask to set an appropriate loading mask to aside element if you wish.
If you don't show loading message to user how user will know what is happening? User will notice that its already loading results so may wait to see the results, but if nothing displayed then user wouldn't know if its bringing new data or not.
You may monit the expand event of the combobox and set picker loading to false.
// in the controller
init: function() {
this.control({
"form combobox[id=fieldId]": {
expand: function(combobox) {
combobox.getPicker().setLoading(false);
}
}
});
}