Dojo 1.4 how to bind an object - javascript

Here is my code:
dojo.provide("test.validation");
dojo.declare("test.validation", null, {
addValidate : function(a) {
this.a = dijit.byId(a);
var link = dojo.connect(dijit.byId("form"), "onclick", this.validate);
},
validate : function(e) {
e.preventDefault();
console.log(this);
if (!this.a.isValid()) {
return false;
}
}
});
I would like call this: this.a.isValid() function, but I'm out of my object scope.
How can I bind it to that onclick event?

Have you tried reading about dojo.hitch()?
It deals exactly with those kind of problems.

Related

How to test if method has been called only once and not the second time in Jasmine?

I am testing mechanism that should call method once and prevent any subsequent calls with jasmine, I can see by attaching breakpoints that method is NOT being called second time however jasmine test fails. I would assume it has to do with spy not being designed to be used for multiple checks.
What would be proper solution to given situation?
JSfiddle of Code that is being tested I could not figure out how to do jasmine test jsfiddle properly (Jasmine version I am using is 1.3.1 while test template is on 1.2.0).
Test looks like this:
it("Invoking OnPreQuery will add event listener for OnTheMoveViewPreLoad event. Triggering OnTheMoveViewPreLoad twice will call getChildrenForMasterRecordList only first time", function () {
AddTreeSettingsObjectToBC({ bc: bc, Tree: { IncludeChildren: true} });
ComposeMockPageObjWithObservableFieldsWithChildren();
var preQuerySpy = spyOnEvent(onTheMove.PageDataRoles, 'OnPreQuery');
$(onTheMove.PageDataRoles).trigger('OnPreQuery', { knockoutContextName: 'bc' });
expect('OnPreQuery').toHaveBeenTriggeredOn(onTheMove.PageDataRoles);
expect(preQuerySpy).toHaveBeenTriggered();
var getChildrenForMasterRecordListSpy = spyOn(window, 'getChildrenForMasterRecordList');
$(onTheMove.PageDataRoles).trigger('OnTheMoveViewPreLoad', { knockoutContextName: 'bc' });
expect(getChildrenForMasterRecordListSpy).toHaveBeenCalled();
$(onTheMove.PageDataRoles).trigger('OnTheMoveViewPreLoad', { knockoutContextName: 'bc' });
expect(getChildrenForMasterRecordListSpy).not.toHaveBeenCalled();
});
Code that is being tested:
HTML
<div data-role="page"></div>
Javascript
var onTheMove = function(){};
$.extend(onTheMove,{
NullValue : "null",
PageDataRoles : 'div[data-role="page"], div[data-role="dialog"]',
OnTheMovePrefix : 'OnTheMove_'
});
$(document).on('OnPreQuery', onTheMove.PageDataRoles, function (e, options) {
var isChildAttachmentQueued = true;
var knockoutContextName = options.knockoutContextName;
if (TreeEnabled(knockoutContextName)) {
var isModelReadyToAttachChildren = function () {
var isReady = false;
if (PageObj[knockoutContextName] != undefined) {
isReady = (PageObj[knockoutContextName]().length > 0) && isChildAttachmentQueued;
}
return isReady;
};
var treeSettings = eval(knockoutContextName).Tree;
treeSettings.knockoutContextName = knockoutContextName;
$(onTheMove.PageDataRoles).on('OnTheMoveViewPreLoad', function (e, options) {
if (isModelReadyToAttachChildren()) {
getChildrenForMasterRecordList({
parentTable: eval(knockoutContextName).primaryTableName,
knockoutContextName: treeSettings.knockoutContextName,
parentIdColumn: treeSettings.ParentIdColumn,
masterIdColumn: treeSettings.MasterIdColumn
});
isChildAttachmentQueued = false;
}
});
}
});
function getChildrenForMasterRecordList(options) {
console.log('beep');
}
toHaveBeenCalledTimes(1)
now makes this much easier.
expect(yourSpy).toHaveBeenCalledTimes(1);
Figured it out myself, spy has property callCount that auto-increments by one on each call.
it("Invoking OnPreQuery will add event listener for OnTheMoveViewPreLoad event. Triggering OnTheMoveViewPreLoad twice will call getChildrenForMasterRecordList only first time", function () {
AddTreeSettingsObjectToBC({ bc: bc, Tree: { IncludeChildren: true} });
ComposeMockPageObjWithObservableFieldsWithChildren();
var preQuerySpy = spyOnEvent(onTheMove.PageDataRoles, 'OnPreQuery');
$(onTheMove.PageDataRoles).trigger('OnPreQuery', { knockoutContextName: 'bc' });
expect('OnPreQuery').toHaveBeenTriggeredOn(onTheMove.PageDataRoles);
expect(preQuerySpy).toHaveBeenTriggered();
var getChildrenForMasterRecordListSpy = spyOn(window, 'getChildrenForMasterRecordList');
$(onTheMove.PageDataRoles).trigger('OnTheMoveViewPreLoad', { knockoutContextName: 'bc' });
expect(getChildrenForMasterRecordListSpy).toHaveBeenCalled();
$(onTheMove.PageDataRoles).trigger('OnTheMoveViewPreLoad', { knockoutContextName: 'bc' });
expect(getChildrenForMasterRecordListSpy.callCount).toEqual(1);
});
as per comment
in Jasmine 2.0 its
expect(object.func.calls.count()).toBe(1);

Generic function for jQuery events

I've got following solution to prevent of multiple clicks (respect only the first one and ignore the rest):
preventMultiClick = function() {
$(this).unbind("click");
$(this).bind("click", function() {
return false;
});
};
preventMultiSubmit = function() {
$(this).unbind("submit");
$(this).bind("submit", function() {
return false;
});
};
$("a").one("click", preventMultiClick);
$("#user").one("submit", preventMultiSubmit);
That solution for me is not elegant I think should be. So I tried upgrade it to following one:
preventMultiClick = function(event) {
$(this).unbind(event);
$(this).bind(event, function() {
return false;
});
};
$("a").one("click", preventMultiClick("click"));
$("#user").one("submit", preventMultiClick("submit"));
and that solution doesn't work. Could somebody explain why or tell me how the function respecting event given as function argument should be written?
The issue is you are calling the function when you are binding the handler, event object is passed to your handler, also event is an object, you should use it's type property.
var preventMultiClick = function(event) {
$(this).unbind(event.type);
$(this).bind(event.type, function() {
return false;
});
};
$("a").one("click", preventMultiClick);
$("#user").one("submit", preventMultiClick);
The problem is that you're passing in an undefined variable rather than the function reference. Instead I would do something like this.
preventMultiClick = function(event) {
$(this).unbind(event.type);
$(this).bind(event.type, function() {
return false;
});
};
$('a').one('click', preventMultiClick);
$('#user').one('submit', preventMultiClick);
Each event contains it's type.

Calling Javascript Functions by name

If I have an element on the page like this ...
<span data-function="DoSomething">Click</span>
... and i put this in my page header ...
$(document).ready(function()
{
$('[data-function]').each(function()
{
var fName = $(this).attr('data-function');
$(this).click(fName);
});
});
... what goes in place of the comment produce the desired effect of executing the function called "DoSomething".
Note:
I no the code above wont work, my question is how to make this work (translate 'DoSomething' in to DoSomething();)
Any ideas guys?
The functions should be available. Try putting them in an Object, like this:
$(document).ready(function()
{
var fns = {
DoSomething: function() {/* ... */},
DoAnotherthing: function() {/* ... */}
};
$('[data-function]').each(function()
{
var fName = $(this).attr('data-function');
$(this).click(fns[fName]);
});
});
Here's a jsfiddle, demonstrating a way to keep everything local to one namespace and assigning handlers based on the data attribute of elements.
Try calling function with window object -
$(document).ready(function() {
$('[data-function]').each(function() {
var fName = $(this).attr('data-function');
if (typeof (window[fName]) === "function") {
$(this).click(window[fName]);
}
});
}
You can use something like
$(this).click(window[fName]);
Where window would be replaced by the appropriate expression if the function DoSomething is not defined in the global scope.
Maybe a little bit clean way:
http://jsfiddle.net/whsPG/
var myfuncs = {
alert : function() {
alert("An Alert");
},
changeName: function(obj) {
$(obj).text('Other Text');
}
};
$('[data-function]').on('click', function()
{
value = $(this).data('function');
if (myfuncs.hasOwnProperty(value)) {
myfuncs[value](this);
}
});
​

Adding a jQuery style event handler of iPhone OS events

I'm looking for a super simple jQuery extension. Basically I need to use some events that jQuery does not explicitly support. These events are the iPhone touch events like ontouchstart, ontouchend, and ontouchmove.
I have it working via this:
// Sucks
$('.clickable').each(function() {
this.ontouchstart = function(event) {
//do stuff...
};
}
Which kind of sucks and is unjqueryish. Here is what I would like:
// Better
$('.clickable').touchstart(function() {
//do stuff...
}
Or even better with 1.4
// Awesome
$('.clickable').live('touchstart', function() {
//.. do stuff
}
These events need no special handling and should work just like any other events, but I can't seem to figure out how to extend jquery to make them work just like all the other events do.
I wrote the plugin, if the user does have touch available, use, otherwise, call click
jQuery.event.special.tabOrClick = {
setup: function (data, namespaces) {
var elem = this, $elem = jQuery(elem);
if (window.Touch) {
$elem.bind('touchstart', jQuery.event.special.tabOrClick.onTouchStart);
$elem.bind('touchmove', jQuery.event.special.tabOrClick.onTouchMove);
$elem.bind('touchend', jQuery.event.special.tabOrClick.onTouchEnd);
} else {
$elem.bind('click', jQuery.event.special.tabOrClick.click);
}
},
click: function (event) {
event.type = "tabOrClick";
jQuery.event.handle.apply(this, arguments);
},
teardown: function (namespaces) {
var elem = this, $elem = jQuery(elem);
if (window.Touch) {
$elem.unbind('touchstart', jQuery.event.special.tabOrClick.onTouchStart);
$elem.unbind('touchmove', jQuery.event.special.tabOrClick.onTouchMove);
$elem.unbind('touchend', jQuery.event.special.tabOrClick.onTouchEnd);
} else {
$elem.unbind('click', jQuery.event.special.tabOrClick.click);
}
},
onTouchStart: function (e) {
this.moved = false;
},
onTouchMove: function (e) {
this.moved = true;
},
onTouchEnd: function (event) {
if (!this.moved) {
event.type = "tabOrClick";
jQuery.event.handle.apply(this, arguments)
}
}
};
$("#xpto").bind("tabOrClick", function () {
alert("aaaa");
});
I've made a small update to Alexandre's plugin to include Android support. Android's browser does not currently support the window.Touch method of detecting touch support.
I love how Alexandre's script waits to ensure movement didn't occur to prevent triggering the event when the user swipes to scroll across the screen. However a downfall of that approach is that it causes its own delay by waiting for the user to lift their finger off of the screen before triggering. I've updated his plugin to include a "touchactive" class that gets applied to items that a user is currently touching. If you take advantage of that class you can provide immediate visual feedback to users without causing an actual event to get triggered until after movement check has completed.
jQuery.event.special.touchclick = {
setup: function (data, namespaces) {
var elem = this, $elem = jQuery(elem);
var ua = navigator.userAgent.toLowerCase();
var isAndroid = ua.indexOf("android") > -1;
if (window.Touch || isAndroid) {
$elem.bind('touchstart', jQuery.event.special.touchclick.onTouchStart);
$elem.bind('touchmove', jQuery.event.special.touchclick.onTouchMove);
$elem.bind('touchend', jQuery.event.special.touchclick.onTouchEnd);
} else {
$elem.bind('click', jQuery.event.special.touchclick.click);
}
},
click: function (event) {
event.type = "touchclick";
jQuery.event.handle.apply(this, arguments);
},
teardown: function (namespaces) {
var elem = this, $elem = jQuery(elem);
if (window.Touch) {
$elem.unbind('touchstart', jQuery.event.special.touchclick.onTouchStart);
$elem.unbind('touchmove', jQuery.event.special.touchclick.onTouchMove);
$elem.unbind('touchend', jQuery.event.special.touchclick.onTouchEnd);
} else {
$elem.unbind('click', jQuery.event.special.touchclick.click);
}
},
onTouchStart: function (e) {
this.moved = false;
$(this).addClass('touchactive');
},
onTouchMove: function (e) {
this.moved = true;
$(this).removeClass('touchactive');
},
onTouchEnd: function (event) {
if (!this.moved) {
event.type = "touchclick";
jQuery.event.handle.apply(this, arguments)
}
$(this).removeClass('touchactive');
}
};
I've also posted this to github in case there are further caveats that are discovered https://github.com/tuxracer/jquery-touchclick
This now works, just like it's stubbed out above, on the latest jQuery release. Go jQuery!
Here's a start:
$.fn.touchstart = function(fn) { return this[fn ? "bind" : "trigger"]("touchstart", fn); };
$.event.special.touchstart = {
setup: function() {
$.event.add(this, "mouseenter", extendedClickHandler, {});
},
teardown: function() {
$.event.remove(this, "mouseenter", extendedClickHandler);
}
};
Where extendedClickHandler is the function that does what it's suppose to do.
More info here: http://brandonaaron.net/blog/2009/03/26/special-events
jQuery.com is a great source of information like this.
If you build your own plugin you'll be able to use whatever naming you like on your method calls.

Delaying default events in Javascript

I would like to be able to delay the default action of an event until some other action has been taken.
What it's for: I'm trying to build a reusable, unobtrusive way to confirm actions with a modal-type dialogue. The key wishlist item is that any Javascript handlers are attached by a script, and not written directly inline.
To make this truly reusable, I want to use it on different types of items: html links, checkboxes, and even other Javascript-driven actions. And for purely HTML elements like links or checkboxes, I want them to degrade gracefully so they're usable without Javascript turned on.
Here's how I would envision the implementation:
Some Link
_________
<script>
attachEvent('a.confirm','click', confirmAction.fire)
var confirmAction = (function(){
var public = {
fire: function(e){
e.default.suspend();
this.modal();
},
modal: function(){
showmodal();
yesbutton.onclick = this.confirmed;
nobutton.onclick = this.canceled;
},
confirmed: function(){
hidemodal();
e.default.resume();
},
canceled: function(){
hidemodal();
e.default.prevent();
}
}
return public;
})()
</script>
I know about the e.preventDefault function, but that will kill the default action without giving me the ability to resume it. Obviously, the default object with the suspend, resume and prevent methods is made up to illustrate my desired end.
By the way, I'm building this using the Ext.Core library, if that helps. The library provides a good deal of normalization for handling events. But I'm really very interested in learning the general principles of this in Javascript.
To resume, you could try saving the event and re-fire it, setting a flag that can be used to skip the handlers that call suspend() ('confirmAction.fire', in your example).
Some Link
_________
<script>
function bindMethod(self, method) {
return function() {
method.apply(self, arguments);
}
}
var confirmAction = (function(){
var public = {
delayEvent: function(e) {
if (e.suspend()) {
this.rememberEvent(e);
return true;
} else {
return false;
}
},
fire: function(e){
if (this.delayEvent(e)) {
this.modal();
}
},
modal: function(){
showmodal();
yesbutton.onclick = bindMethod(this, this.confirmed);
nobutton.onclick = bindMethod(this, this.canceled);
},
confirmed: function(){
hidemodal();
this.rememberEvent().resume();
},
canceled: function(){
hidemodal();
this.forgetEvent();
},
rememberEvent: function (e) {
if (e) {
this.currEvent=e;
} else {
return this.currEvent;
}
},
forgetEvent: function () {
delete this.currEvent;
}
}
return public;
})()
// delayableEvent should be mixed in to the event class.
var delayableEvent = (function (){
return {
suspend: function() {
if (this.suspended) {
return false;
} else {
this.suspended = true;
this.preventDefault();
return true;
}
},
resume: function () {
/* rest of 'resume' is highly dependent on event implementation,
but might look like */
this.target.fireEvent(this);
}
};
})();
/* 'this' in event handlers will generally be the element the listener is registered
* on, so you need to make sure 'this' has the confirmAction methods.
*/
mixin('a.confirm', confirmAction);
attachEvent('a.confirm','click', confirmAction.fire);
</script>
This still has potential bugs, such as how it interacts with other event listeners.

Categories