I am using uitest.js (built-on Jasmine.js) to test our Kendo UI Mobile application. I am displaying an actionsheet and programmatically selecting on of the options. This works ok in the app but throws an error that fails the test.
I am using an action sheet like this:
<ul data-role="actionsheet" id="marketplace-price-actions" >
<li class="km-actionsheet-title">Select Price</li>
<li>$$$</li>
<li>$$</li>
<li>$</li>
</ul>
and In my spec I am selecting one of the options like this:
$("#marketplace-price-actions li a").eq(2).mousedown().mouseup();
and this works, yet throws the following error:
TypeError: "undefined is not a function"
I have created a jsfiddle that displays this at http://jsfiddle.net/zkent/DD6vj/2/. Be sure to open the console.
EDIT Based on the selected answer, the error was from passing values to the functions. I chose to create separate callbacks. See http://jsfiddle.net/zkent/DD6vj/4/.
It looks like you're not supposed to pass parameters to your action. I'm not sure why it's implemented this way, but this is causing your error (which also happens if you simply click on it, so it's not related to Jasmine).
As far as I can see, you have three options:
Use a separate callback for each item
Modify the ActionSheet source code to supply the clicked element to your action
Use a closure over the value you pass and return a handler
Option 3 seems to be the best solution if you only need to pass one value but want to avoid code repetition due to multiple handlers.
Please note that I haven't tested the following solutions at all, so use at your own risk.
For option #2, something like this might work:
kendo.mobile.ui.ActionSheet.fn._click = (function (click) {
return function (e) {
if (e.isDefaultPrevented()) {
return;
}
var action = $(e.currentTarget).data("action");
if (action) {
kendo.getter(action)(window)({
target: this.target,
context: this.context,
element: e.currentTarget // pass in the element that was clicked on
});
}
e.preventDefault();
this.close();
}
})(kendo.mobile.ui.ActionSheet.fn._click);
That way you'd at least know which element was clicked on and you could add data attributes to pass data, if you wanted, e.g.:
<li>$$</li>
which you could then read in your handler:
function alertme(e) {
console.log(e);
console.log($(e.element).attr("data-value"));
}
(demo)
For option #3, you would simply define your action as:
function alertme(val) {
return function(e) {
console.log(e);
console.log(val);
};
}
and your element would be as it was:
<li>$$
(demo)
Related
I use jqInlineEdit for inline editing on a web page. Everything works, except I don't know how to get the id of the item which I need for saving the change to the database(via Django).
The HTML looks like this:
<div id="remark14756" class="remark" data-cid="14756">
Sample Text
</div>
That's the JavaScript:
<script src="/static/inline-edit.jquery.js"></script>
<script>
$(".remark").inlineEdit({
type: 'textarea',
onChange: function (e, text, html) {
// Executes when exiting inline edit mode and a change has been made
c_id = $(this).attr("data-cid");
alert("Test: ", c_id)
}
});
</script>
Obviously, $(this) does not work in this context. I tried everything and searched a lot but I can't find how to do it the right way. Does anybody know the answer?
The inlineEdit docs say:
onChange(this, text, html) - Executes when exiting inline edit mode and a change has been made
with the use of this being quite misleading.
therefore the first param is actually the Element.
$(".remark").inlineEdit({
type: 'textarea',
onChange: function (elem, text, html) {
// `this` refers to inlineEdit instance plugin
// `elem` is the currently edited element
const c_id = $(elem).attr("data-cid");
alert(c_id); // 14756
}
});
That plugin is not performing in an expected "jQuery Plugin" way.
Usually properly written plugins should:
bind all methods to the Element callee,
(in case of Event methods) the first parameter should always refer to the original Event.
allowing a developer to reference it using the this keyword to get the native JS Element or either doing $(this) inside the exposed public Methods just like we're expected from native jQuery Methods, and to have accessible the Event (i.e: useful in case we use arrow functions to extract the currentTarget since the inexistence of this keyword)
$someElem.on('click', function(evt) {
const $el = $(this); // what we're used to
});
$someElem.on('click', (evt) => {
const $el = $(evt.currentTarget); // the importance of always passing the Event as first param
});
clearly not implemented in that plugin.
I need to include the download icon inside the tooltip message, I have tried the below code:
ObjectIconView: Ember.ContainerView.extend(childMOMixin, {
iconDownload: function () {
model: 'download'
},
mouseEnter: function(e) {
if(type == 'Text'){
var textUrl = {content: 'Preview is Not Available, Use '+ this.iconDownload() +'Menu'};
this.$().tooltip(textUrl);
}
}
In that I have called the iconDownload inside the tooltip. But it's saying undefined in the output. I'm using Ember 1.4.0 version. Can anybody please provide the suggestion for this. I'm new to the Ember. Thanks in advance
There are two things that you need to change to get the tooltip to display the data you're expecting.
1: Return something from iconDownload
Right now, the function returns nothing, it makes an internal list and then does nothing.
Does it even need to be a function, or could it just be a string, object?
2: You're not accessing the data in it correctly.
Assuming you're actually needing a hash returned, you're not accessing the data properly - all you're doing is getting the object.
With the structure you have right now, you'r string generator would be
'Preview is Not Available, Use '+ this.iconDownload().model +'Menu'
I'd recommend a couple of additional changes, if they're possible.
1: Use the Ember getter to get data from iconDownload.
Instead of this.iconDownload, call this.get('iconDownload.model') or this.get('iconDownload')
2: Make the actual tooltip text a computed property.
toolTipText: function () {
return `Preview is Not Available, Use ${this.get('iconDownload.model')} Menu`;
}.property('iconDownload.model');
Now, you can just call this.get('toolTipText') to get what the tooltip says.
3: Move your mouseEnter function into the actions section of your view's javascript
I'm using the vex dialog library basically as a replacement for standard alert, confirm, prompt etc boxes and I have been using them like this for example:
$('.js-edit-cancel').on('click', function(e) {
e.preventDefault();
vex.dialog.buttons.YES.text = t('Yes');
vex.dialog.buttons.NO.text = t('No');
vex.dialog.confirm({
message: t('Are you sure you want to cancel?'),
callback: function(value) {
if (value) {
// Some code here...
}
}
});
});
Now whilst this works, it starts to become a little repetitive when you are using the dialog boxes for several things.
Ideally I could simply add HTML5 data-* attributes to any element I wanted one on, something like data-confirm-box or data-prompt-box; however problem with this method is I also need to be able to set custom messages rather than always having it set to Are you sure you want to cancel? as well as being able to supply a callback function to run; I guess technically I could supply this data as extra data-* attributes, but it just seems a bit messy to me.
Then I thought I could keep doing click events for each element I wanted it on and passing in the custom message, but I guess I would also have to pass in a callback this way and then this would end up with having two code blocks each time you want to use one; the click event handler and then the callback function.
Is there any cleaner way to do what I want to do - be able to set a custom message as well as an optional callback / custom code each time I want a confirmation box?
The library itself is vanilla JavaScript but I'm also using jQuery as you can see so I'm happy using either.
You may declare and use a unified function(let's say showDialog(type, options)) passing in the type of dialog and a custom options object:
function showDialog(type, options) {
if (typeof type !== 'string'
|| ['alert','prompt','confirm','open'].indexOf(type) === -1) {
throw new Error('Wrong dialog type!');
}
// You can specify your own buttons if you want more options than just OK or Cancel.
// If you simply want to change the labels, you can override the default options
// You can also specify a static message text for a certain dialog type
vex.dialog[type](options);
}
// Approximate usage:
...
$('.js-edit-cancel').on('click', function(e) {
e.preventDefault();
showDialog('confirm', {
message: t('Are you sure you want to cancel?')
});
});
...
$('.some_element').on('click', function(e) {
e.preventDefault();
showDialog('alert', {
message: t("You don't have permission on this action!")
});
});
...
$('.some_other_element').on('click', function(e) {
e.preventDefault();
showDialog('open', {
message: t('Select date and time:'),
callback: function(data) {
if (data) {
// Some code here...
}
}
});
});
Question about a bit more advanced version of BackboneJS Todo List
BackboneJS Todo List http://todomvc.com/examples/backbone/
On click "X" link I have simple handler (events['click .destroy'] = 'clear',):
clear: function () {
this.model.destroy()
},
As I can see, I can do something like this:
clear: function () {
this.model.destroy()
.fail(function(err) {})
.done(function(resData) {
// HERE I would like to cancel firing other events, like "destroy".
})
},
In done callback I can get info if there was some problem while deleting row from table. In current case - it is Doctrine2 exception related to Foreign Key Constraint. I catch it in my todos.php, set {success: false}, and wish in done callback use it to prevent HTML element "li" to be deleted from Todo list, as it happens now.
Is that so, when method model.destroy is called, no matter failed it or succeded, event "destroy" is fired, and because of that - element "li" is been removed from Todo List ?
Maybe I should use "request" event for collection?
What technique is best practice?
Could you please paste here some example (or link to example)?
A few weeks ago I was painfully able to dynamically add buttons to an HTML DOM object that has its own .on('click'.. handler, and use e.stopPropgation to stop these new child elements from firing the event.
The weird thing I did was call a function without any parenthesis. I have no idea why I did this or why it works, or why it does not work when I do attach parenthesis. I want to know if I am doing something by fluke and not design (and now I will add comments to it).
It goes as such:
//Container is the parent element
// var buttons stores the buttons with class 'buttons'
$('.container').on('click', function(){
$(buttons).appendTo($(this)).fadeIn(500).find('.buttons').click(tableButton);
});
function tableButton(e){
e.stopPropagation();
//do stuff
}
I can't figure out why I wrote the call to tableButton with no arguements or why it works perfectly. I tried to change the syntax to
.find('.buttons').on('click', function(e){
tableButton(e);
});
but then it no longer works.
Any help appreciated!
It works because you're passing a function to the click handler rather than calling the function yourself (the ()) An example of that:
var testFunction = function(msg) {
alert(msg);
}
var functionCaller = function(functionToCall) {
functionToCall('hello!');
}
functionCaller(testFunction);
functionCaller passes the message argument to testFunction(), but we only pass testFunction to functionCaller (without arguments)
For the part which doesn't work, isn't the function name tableButton() instead of tableButtons()?
See http://jsfiddle.net/g2PAn/
You don't actually call it, you just declare it and the arguments it accepts. The click callback is called with an argument indeed, but not by you.
The problem probably comes from the fact that jQuery calls your function with the element clicked bound as this, you could call table button like this:
.find('.buttons').on('click', function(e){
tableButton.call(this, e);
});