javascript context changing using jQuery.fn.extend - javascript

I try to create a method that can be called from the created DOM element corresponding to my B class, like that:
$("#" + data.selectedId).eventCallback(data);
My eventCallback is defined in a mother class A, as you can see in the following code:
function A (){
this.addToPage = function () {
...
var context = this;
// This call is well overridden
context.onEvent("toto");
jQuery.fn.extend({
// This call uses A's method every time
eventCallback: function (data) {context.onEvent(data);}
});
};
this.onEvent = function (data) {
console.log("to override");
};
}
function B (){
this.onEvent = function (data) {
console.log("overridden");
};
}
B.prototype = Object.create(A.prototype);
The problem is, as commented, that I seem to not being able to use the same context when I enter the block jQuery.fn.extend. Is there a better (and cleaner) way to do it? Do I miss something?
Edit, to clarify:
The A class define the structure of widgets which are set in my html document (so to my mind an A instance is somehow linked to a part of the DOM).
As a user, I want to be able to select a widget that call a method (which definition is depending on B).
So My idea was to implement a callBack in the class and then make it callable from the DOM objects created with an A instance.

Related

Add computedObservable to existing javascript view model

I have the following ViewModel.
function PageSectionVM(pageSection) {
var self = this;
self.SectionName = ko.observable();
self.Markup = ko.observable();
self.update(pageSection);
}
and I have also created the update method as called in the above constructor function.
PageSectionVM.prototype.update = function (pageSection) {
var self = this;
pageSection = pageSection || {};
self.SectionName(pageSection.SectionName);
self.Markup(pageSection.Markup);
};
This is bundled off in its own file and I would like to reuse this VM in several pages. On one particular page I would like to 'extend' this viewmodel to include a new function. I have tried do this by adding a new function to PageSectionVM's prototype, like so.
PageSectionVM.prototype.tabName = function () {
var self = this;
return "#tab-" + self.SectionName();
};
If I then add this as a knockout binding statement, it returns the text of the function as opposed to the function result. I get the feeling I am missing something. If I add tabName as a computedObservable in the original viewmodel it works, but that means I am putting specific code for a single purpose in my 'general' viewmodel's code (something I'd like to avoid).
The knockout binding statement I am using is
<a data-bind="attr:{href: tabName}, text:SectionName"></a>
This sits inside a foreach binding on an observableArray of PageSectionVMs. The text property is fine, but the href ends up containing the literal text of the function, not its result.
Any help greatly appreciated.
If I then add this as a knockout binding statement, it returns the text of the function as opposed to the function result.
Of course it does. Knockout bindings work like this:
check if the bound value is an observable
if yes, unwrap it (i.e. "execute it")
convert to string what remains and use it in your view
NB: All observables are functions, but not all functions are observables.
That means you will get the function text if your binding looks like text: SectionName when SectionName() is a plain old function.
Because of the way observables work (w/r/t dependency tracking & this handling) you cannot use them in a prototype, they have to live in the instance.
That means:
either you put them in the instance (like #WayneEllery suggests)
or you call them yourself in the view (text: SectionName())
or you use ko.utils.extend() to extend pre-existing PageSectionVM instances with extra observables/computeds.
I don't understand why you have a problem using a computed. If you want to use prototype you can do it as follows:
I've also added url encoding and initilized the model.
function PageSectionVM(pageSection) {
var self = this;
self.SectionName = ko.observable(pageSection.SectionName);
self.Markup = ko.observable(pageSection.Markup);
self.TabName = ko.computed(this.getTabName, self);
};
PageSectionVM.prototype.getTabName = function () {
var self = this;
return "#tab-" + encodeURIComponent(self.SectionName());
};
http://jsfiddle.net/5vUhe/

Change jquery selection scope

When I am writing a jQuery selectors within a specific-page javascript, I'd like to be able to use a simplified (scoped) selection mechanism.
To reference the element currently I need to use the full selector:
$('#home-view #events-cloud')'
Since the code line above is used within a home-view backing js file, I'd like to be able to do something like this:
$.SetSelectorPrefix('#home-view');
$('#events-cloud');
Is there a possibility to do something to address the code-location specific selections?
Simply modify the default jQuery init function so that the context is the one chosen by you:
jQuery.noConflict();
$ = function (selector, context) {
return new jQuery.fn.init(selector, context || document.getElementById('home-view'));
};
$.fn = $.prototype = jQuery.fn;
jQuery.extend($, jQuery);
console.log($('#events-cloud'));
Explanation:
jQuery.noConflict();
This line prevent jQuery's default assign to $.
$ = function (selector, context) {
return new jQuery.fn.init(selector, context || document.getElementById('wrapper'));
};
Assign to $ the new modified function that will return a new jQuery instance with the context modified for your purposes(this is the clue of the script)!
$.fn = $.prototype = jQuery.fn;
Creates a new property fn to the $ function, and assign it to the prototype inheriting properties from jQuery.fn.
jQuery.extend($, jQuery);
Extends the created object with jQuery functions, so you can use it exactly as jQuery.
See a working fiddle of this snippet.
You could use something like this:
var container = $('#home-view');
// Will only search elements within #home-view
container.find('#events-cloud');
// Changing container
container = $('#main-view');
// Will only search elements within #main-view
container.find('anotherSelector');
I suppose you need genericity on your generic sections, otherwise you would not ask.
So a generic code could call $('#selector') without being aware of the context.
The simplest way: use a custom function $$
function $$(selector, context) {
return $(selector, context ? $(context, $$.stack[0]) : $$.stack[0]);
}
$$.stack = [];
$$.push = function(context){ $$.stack.unshift(context); }
$$.pop = function(){ return $$.stack.shift(); }
In your generic sections use $$('#selector') instead of $('#selector')
Around your generic code execution use:
$$.push('#home-view');
// generic code call using $$
// e.g. $$('#events-cloud') <=> $('#events-cloud', '#home-view') <=> $('#home-view #events-cloud')
$$.pop();
With this custom function, you can nest contexts and even use the $$(selector, localcontext) form. Thus for the generic code, the current global context is totally transparent.

javascript: anonymous function expose functions (how frameworks really works)

i was exploring in the last few days how big frameworks works , how they assign their function name and it can't(?) be override , i pretty much know how framework work with anonymous function , for example they do it this way or similar version :
(function(){
var Sizzle = function (){
var x;
};
Sizzle.f = function(){
alert("!");
};
window.Sizzle = Sizzle;
})();
i still don't get few things about those huge frameworks and i hope i can find answer :
how do they assign function name and the name can't be override?
in the code above to call the function i need to write Sizzle.f() to get the function to work , but when i use jquery i don't write Jquery.show() , just show() , how do they vanish the "jquery" from "jquery.show()" function call?
by saying the name can't be override i mean , if i create function with one of the jquery functions names , the jquery function will work.
thanks in advance.
As has been shown for #2, it's really easy for BIG_NAMESPACE.Functions.doStuff to be added to anything you want.
var _ = BIG_NAMESPACE.Functions.doStuff;
_(); // runs BIG_NAMESPACE.Functions.doStuff;
As for #1:
Most libraries DO let their functions be overwritten.
It's the values that are inside of the framework's closure which are preserved, for safety reasons.
So you could do something like:
BIG_NAMESPACE.Functions.doStuff = function StealEverything() {};
(BIG_NAMESPACE.Functions.doStuff === StealEverything) // true;
But doStuff would have NO access to any of the variables hidden inside of the framework's closure.
It would also mean that until the page was reloaded, doStuff would also not work the way you want it to.
HOWEVER, in newer versions of JavaScript (ECMA5-compatible browsers), it WILL be possible to do something like what you're suggesting.
BIG_NAMESPACE = (function () {
var do_stuff = function () { console.log("doin' stuff"); },
functions = {
set doStuff (overwrite) { }
get doStuff () { return do_stuff; }
};
return { Functions : functions };
}());
Then, this will work:
BIG_NAMESPACE.Functions.doStuff(); // "doin' stuff"
BIG_NAMESPACE.Functions.doStuff = function () { console.log("ain't doin' jack"); };
BIG_NAMESPACE.Functions.doStuff(); // "doin' stuff"
However, Frameworks aren't going to use this for a LONG time.
This is not even remotely backwards compatible. Maybe in 2016...
There were defineGetter and defineSetter methods as well, but they aren't a formal part of the JavaScript language. Like innerHTML, they're things that the browser vendors put in, to make life better... ...as such, there's no real guarantee that they're going to be in any/all browsers your users have. Plus, they're deprecated, now that new browsers use the get and set constructs that other languages have.
(function(){
var jqueree = {};
jqueree.someval = 22;
jqueree.somefunc = function(){ alert(this.someval); };
window.jqueree = jqueree;
window.somefunc = function(){ jqueree.somefunc.call(jqueree); };
window.$$$ = jqueree;
})();
// all equivalent
window.somefunc();
window.jqueree.somefunc();
$$$.somefunc();
somefunc();
Answering your Questions
At the top of jQuery you'll see: var jQuery = (function() {, which creates the local function (its incomplete; the }); occurs elsewhere).
At the very end of jQuery you'll notice the following, which is how it attaches it to the global namespace:
// Expose jQuery to the global object
window.jQuery = window.$ = jQuery;
I have never seen a jQuery function called without referencing the jQuery object. I think you always need to use jQuery.show() or $.show(); however maybe you're saying you don't have to call window.jQuery.show(), which you are permitted to drop the window, since that is the default.
Using your example
(function(){
/* This is where Sizzle is defined locally, but not exposed globally */
var Sizzle = function (){
var x;
};
/* If you put "window.f = Sizzle.f = function(){" then you could *
* call f() w/o typing Sizzle.f() */
Sizzle.f = function(){
alert("!");
};
/* The following line is what makes it so you can use Sizzle elsewhere *
* on your page (it exposes it globally here) */
window.Sizzle = Sizzle;
})();
use function _name_() {} and the name is static
the simply use var $ = jQuery; to create an alias.
jQuery works this way:
Supposed you have this jQuery code:
$("#title").show();
You have three elements to that line.
$ is a javascript function
"#title" is an argument to that function
.show() is a method call
Here's how it works.
Javascript executes the function named $ and passed it an argument of "#title".
That function does it's business, finds the #title object in the DOM, creates a jQuery object, puts that DOM element into the array in the jQuery object and returns the jQuery object.
The Javascript execution engine then takes the return value from that function call (which is now a jQuery object) and looks for and executes the .show() method on that object.
The .show() method then looks at the array of DOM elements in the jQuery object and does the show operation for each DOM element.
In answer to your question, there is no .show() all by itself. It's a method on a jQuery object and, in this example, that jQuery object is returned from the $("#title") function call.

jQuery Pattern - is this valid or is there a better way?

I've sort if fell into this organization of javascript and was wondering if I'm missing the point somewhere here, or if there's a more elegant way of doing this.
Basically I'm wrapping everything in a function (object) and then setting up methods on that object, then instantiating an instance of the wrapper object and passing in any options and dependencies.
I have a hunch there's a way to automatically run .init() and a few other tweaks that could be made. Am I doing it right?
function AppModuleCore(){
var AppModuleCore = this; //keep internals sane
// Various global vars, objects
AppModuleCore.defaultOptions = {};
AppModuleCore.init = function(opts) {
// todo: that thing where you extend an options object a la juery
AppModuleCore.bindEvents();
};
AppModuleCore.bindEvents = function() {
// bind events here, send to functions within AppModuleCore.<FUNCTIONNAME>();
// Example:
$("a#clicker").unbind("click");
$("a#clicker").click(function(event){
AppModuleCore.handleClickerClick(event);
});
};
AppModuleCore.handleClickerClick = function(event){
alert("clicker was clicked");
};
}
// --------------------------------------------------------------------
// instantiate AppModuleCore object and initialize with opts,
// dependency injection
// --------------------------------------------------------------------
$(document).ready(function(){
AppModuleCore = new AppModuleCore;
var options = {};
AppModuleCore.init(options);
});
OK, some points
Having your code wrapped in a constructor only really makes sense if
You're going to instantiate more than one
You have "public" methods on the object that you are going to call
Your code doesn't exhibit these characteristics. I say this because your jQuery selectors a#clicker are hard coded so I'm assuming that you wouldn't want to bind the same events to them more than once?
You'd be better off using a function (perhaps your init) or an object literal to limit your scope..
function init( options ) {
var defaultsOptions = {};
var privateVar = 'only in this scope';
//extend your default options with options here
//using jquery
options = $.extend( defaultOptions, options );
// this function is completely private to this scope
function privatefunction() {
//do stuff
}
function handleClickerClick( event ){
alert("clicker was clicked");
}
// you don't need to wrap your handler in an anonymous function unless
// you're doing some work to the event before forwarding:- just give a
// reference to your handler
// the handler has access to other members of this scope, we're in a closure
$(options.selector).click( handleClickerClick );
//etc
}
init( {selector: 'a#clicker'} );
On a stylistic note: when you alias this with the same name as the constructor and then add methods to the alias, it looks at first glance like you are adding static methods to the constructor. This may be confusing to someone who looks at your code later and doesn't notice the alias.
function C() {
// a static method i.e a property of the constructor, C not objects created with it
// it is a bit wierd that it is defined in the constructor but not unheard of
C.staticMethod = function(){};
//quite plainly a method of objects of this type, easy to understand
this.method = function(){};
}

Javascript Prototype not Working

Hi I don't know whether this is my mistake in understanding Javascript prototype object ..
Well to be clear I'm new to the Javascript singleton concept and lack clear cut knowledge in that but going through some referral sites I made a sample code for my system but it's giving out some errors which I couldn't find why so I'm asking for your help. My code is:
referrelSystem = function(){
//Some code here
}();
Prototype function:
referrelSystem.prototype.postToFb = function(){
//Some Code here
};
I get an error saying prototype is undefined!
Excuse me i thought of this right now
EDIT
I have used like this:
referrelSystem = function(){
return{
login:getSignedIn,
initTwitter:initTw
}
};
Is this causing an issue?
A typical way to define a JavaScript class with prototypes would be:
function ReferrelSystem() {
// this is your constructor
// use this.foo = bar to assign properties
}
ReferrelSystem.prototype.postToFb = function () {
// this is a class method
};
You might have been confused with the self-executing function syntax (closures). That is used when you would like to have "private" members in your class. Anything you declare in this closure will only be visible within the closure itself:
var ReferrelSystem = (function () {
function doSomething() {
// this is a "private" function
// make sure you call it with doSomething.call(this)
// to be able to access class members
}
var cnt; // this is a "private" property
function RS() {
// this is your constructor
}
RS.prototype.postToFb = function () {
// this is a class method
};
return RS;
})();
I would recommend that you study common module patterns if you're looking into creating a library.
Update: Seeing your updated code, the return from referrelSystem won't work as expected, since return values are discarded when calling new referrelSystem().
Rather than returning an object, set those properties to this (the instance of referrelSystem that gets constructed):
var referrelSystem = function () {
// I assume you have other code here
this.login = getSignedIn;
this.initTwitter = initTw;
};
I don't think you intend to immediately execute the functions, change them to this:
var referrelSystem = function(){
//Some code here
};
(+var, -())
Same with the prototype function:
referrelSystem.prototype.postToFb = function(){
//Some Code here
};
(Here you don't need the var, because you're assigning to something that already exists.)
A function should return to work as
prototype
property.
Take a look at this example here

Categories