Javascript inheritance on variable within a function (OpenERP) - javascript

Basically I'm trying to override a function by extending it. I have the following base (simplified) code:
openerp.point_of_sale = function(db) {
var Order = Backbone.Model.extend({
exportAsJSON: function() {
return {'bigobject'}
}
})
}
Then, I'm writing my own .js where I want to inherit and override exportAsJSON function and I'm not sure how to .extend it. Here is my erroneous approach:
openerp.my_module = function(db) {
db.point_of_sale.Order = db.point_of_sale.Order.extend({
exportAsJSON: function() {
var order_data = this._super();
//... add more stuff on object
return order_data;
}
})
}
What would be the correct way of doing it?
I hope I'm providing enough information for an answer (I'm working on OpenERP by the way). Any help will be appreciated.
EDIT:
More specifically, the error seems to be in the extension itself:
db.point_of_sale.Order = db.point_of_sale.Order.extend({
...even if I put a simple return 0; within my exportAsJSON function, the page doesn't load and I get the following error in my browser console:
"Cannot call method 'extend' of undefined"

I think you want something like SuperClass.prototype.method.call(this):
openerp.my_module = function(db) {
db.point_of_sale.Order = db.point_of_sale.Order.extend({
exportAsJSON: function() {
var order_data = db.point_of_sale.Order.prototype.exportAsJSON.call(this);
//... add more stuff on object
return order_data;
}
})
}

This is how you would normally do that in JavaScript:
var eaj = db.point_of_sale.Order.prototype.exportAsJSON;
db.point_of_sale.Order = db.point_of_sale.Order.extend({
exportAsJSON: function() {
var order_data = eaj.apply( this, arguments );
//... add more stuff on object
return order_data;
}
})

This is basically where you problem lies:
openerp.point_of_sale = function(db) {
var Order = Backbone.Model.extend({
^
|
this is a private variable
not a property!
Therefore you cannot access it at all. If it was defined like this:
openerp.point_of_sale = function(db) {
openerp.point_of_sale.Order = Backbone.Model.extend({
^
|
this is now a property of point_of_sale
(basically public variable)
then you can access it the way you're trying to:
db.point_of_sale.Order = db.point_of_sale.Order.extend({
So, the answer is you cannot do that. You need to extend or modify db.point_of_sale instead of Order.

Related

Extending a function and instantiating it - javascript

--
Hello SO, Today i come before you with a humble question, As I'm obviously missing something fairly basic.
I'm trying, And I can't see why it shouldn't work, To "extend" a function.
To be specific, Consider the following code :
It appears that variables have gone missing from the console.log even though they're defined.
However this doesn't seem like the right way to implement what i'm trying to achieve.
The requirement : `Extending a function with variables and methods so that all new instances of that function will receive those variables. What you could consider 'class variables'.
Super = function (){}; //yes it's global.
Super.prototype.alert = function()
{
console.log('alert function');
}
ExtendSuper = function(arguments) //yes it's global
{
**EDIT , THIS IS THE ANSWER THANKS TO YURY**
return function () {
return new Super(arguments);
}
}
arguments = {} //some object with variables and functions
ExtendedFunction = ExtendSuper(arguments); //yes it's global
success = new ExtendedFunction();//yes, it's global
EDIT: OP has changed the question in a way making code example irrelevant. Good for him!
You have some weird ideas about inheritance actually. I do recommend you to rethink your application before its too late. :) You do need prototypes because they are essential part of javascript.
Anyway http://jsfiddle.net/uj4ag/
var DomDom = (function(){ //Do not need a function? Use IEFE
function View(bootstrap) //my "view" class
{ var view = this;
view.$elm = false; view.model = false;
view.render = function()
{
console.log('rendering something');
}
$.extend(view,bootstrap);
};
return {
View: View,
extend: {
View: function(params) {//Let's create a new function :)
return function() { //it is not an inheritance it is 'currying'
return new View(params);
}
}
}
}
}());
var SubClass = DomDom.extend.View({
foobar : true,
alert : function () { alert('hi')},
render : function() { console.log('rendering something else')},
});
var viewInst = new DomDom.View;
var subClassInst = new SubClass();
viewInst.render();
subClassInst.render();

Initialize custom ko.computed in class

I don't know if it's possible, but I would like to achieve something like this:
function templateCheck(Properties,ComputedFunction){
var self = this;
self.Prop1 = ko.observable(Properties.Prop1);
self.CustomCaltulation = ko.computed(ComputedFunction);
return {
returningValue: self.CustomCalculation
}
}
// This doesn't work
var test = new templateCheck({Prop1: "something"},function(){ return self.Prop1(); })
// Error: TypeError: Object [object global] has no method 'Prop1'
Thus far I'm unsuccessful to achieve this.
There's no need for a 'self' variable. It's actually making it harder for you, since 'self' is not defined outside the context of templateCheck, as you found out. The trick is to use the 'owner' property of the computed variable, which sets the 'meaning' of this
function templateCheck(Properties, ComputedFunction){
this.Prop1 = ko.observable(Properties.Prop1);
this.CustomCalculation = ko.computed({
read: ComputedFunction,
owner: this
});
return {
returningValue: this.CustomCalculation
};
}
var test = new templateCheck({Prop1: "something"},function(){ return this.Prop1(); })
I do wonder why you are trying to do this though. It feels a bit like a code smell to me. Maybe there's a better way?
Your problem is that the self variable is not defined in the context of your function.
function templateCheck(Properties){
var self = this;
self.Prop1 = ko.observable(Properties.Prop1);
self.setComputedFunction(computedFunction) {
self.customCalculation = ko.computed(computedFunction);
}
}
var template = new templateCheck({Prop1: "something"});
var myComputedFunction = function(){ return template.Prop1(); };
template.setComputedFunction(myComputedFunction);
var test=template.customCalculation;
You have written self.CustomCaltulation instead of self.CustomCalculation . This might not be the issue in your case but soon i see your question i found this.

How to Properly Access Class Properties in Javascript

I have this class definition:
$.note = function() {}
$.note.prototype = {
init: function(note) {
this.note = note;
this.ctrl = document.getElementById(note);
},
// I have these getter functions because I was getting errors using
// myObject.note or myObject.ctrl
getNote: function() {
return this.note;
},
getCtrl: function() {
return this.ctrl;
}
}
I created a new object with this class like this:
var note = new $.note('C');
Which I can access in my console like this:
But when I try and access note.getNote(), I get undefined as the response:
Am I going about accessing these properties incorrectly? I've tried using just note.note or note.ctrl, and I get the same thing...
Nothing's going to call that "init" function if you don't.
$.note = function(note) { this.init(note); }
Some frameworks provide an object system that uses constructor helper functions like that, but plain JavaScript doesn't.
Try this:
$.note = function(note) { this.note = note;}
or you should call init function:
var note = new $.note();
note.init('C');

Improving the implementation of a plugin architecture

I have an extend method included in my library, making it possible for methods to be added to the core library:
library.prototype.extend = function(name,plugin) {
library.prototype[name] = plugin.init;
for (var method in plugin) {
if(method !== 'init') {
library.prototype[name].prototype[method] = plugin[method];
}
}
};
In use it looks like so:
library.prototype.extend('aReallyInterestingPlugin', {
//the init method gets added to the base libraries prototype
init: function() {
this.shouldBePrivate;
},
//another other methods are created as a prototype of the newly added Plugin
anotherMethod : function() {
//this should have access to the shouldBePrivate var
}
});
Users are then able to call the plugin like so:
var test = new library();
test.aReallyInterestingPlugin();
This works but I'm not exactly happy with the approach and have been trying to find an alternative pattern to make this work.
The problem with it, is that the init and the anotherMethod are added directly to the libraries prototype chain so their scope is also the global libraries scope which is messy because if any instance variables are declared (like shouldBePrivate above) they are also added to the libraries prototype chain.
How can I enable the plugin to be added and have it's own private scope? One way I've thought of is that a plugin could always be called as a constructor (and will therefore have it's own scope and this context) but then I'm not sure how clean that is...for instance for that to work the user would have to do something like this when calling the plugin:
var test = new library();
test.aPlugin = new aReallyInterestingPlugin();
What you could do is to have the plugin method bound to a new object, so that they don't 'pollute' the Library.
But instead of having the plugin as a method of a Library instance, have it rather as a lazy getter, so the syntax is more fluid, and you can build a new instance only if required.
The plugin syntax could then be simplified : just use a javascript class => a function that has methods defined on its prototype.
I think also that it makes sense to have 'extend' as a property of Library, not a method set on its prototype, since no instance should make use of it.
With this you can add a plugin with
library.extend('aReallyInterestingPlugin',AReallyInterestingPluginClass);
to use you can write
var myLibrary = new Library();
myLibrary.somePlugin.someMethod(arg1, arg2, ...);
The code would look like :
library.extend = function(name,plugin) {
var pluginInstance = null; // lazy evaluation to avoid useless memory consumption
var pluginGetter = function() {
if (pluginInstance == null) pluginInstance = new plugin();
return pluginInstance; };
Object.defineProperty( Library.prototype, name,
{ get: pluginGetter, enumerable : true } );
} ;
A plugin is just standard javascript class:
function MyPlugin() {
this.pluginProperty1 = 'some value';
}
MyPlugin.prototype = {
method1 : function() { /* do things */} ,
method2 : function() { /* do other things */ }
};
Notice that with this scheme the plugin is a singleton, i.e. every instances of Library will return the same object when asked for the same plugin.
If you prefer once plugin instance per Library, just have the Library constructor hold the plugins instances. (maybe in a hidden property).
function Library() {
// the code allready here...
var pluginInstances = {};
Object.defineProperty(this, 'pluginInstances',
{ get : function() { return pluginInstances }, enumerable : false });
}
library.extend = function(name,plugin) {
var pluginGetter = function() {
if (! this.pluginInstances[name] ) this.pluginInstances[name] = new plugin();
return this.pluginInstances[name];
};
Object.defineProperty( Library.prototype, name,
{ get: pluginGetter, enumerable : true } );
} ;
syntax for the plugin and for the use remains the same.
Edit : for older Browser support, you can still use a function instead of a getter :
function Library() {
// the code allready here...
this.pluginInstances= {} ;
}
library.extend = function(name,plugin) {
Library.prototype[name] = function() {
if (! this.pluginInstances[name] ) this.pluginInstances[name] = new plugin();
return this.pluginInstances[name];
};
} ;
to use it you would do :
var myLibrary = new Library();
myLibrary.somePlugin().someMethod(arg1, arg2, ...);
Edit 2 : version with singleton plugins and with no getters is :
function Library() { /* same code */ }
library.extend = function(name,plugin) {
var pluginInstance = null; // lazy evaluation to avoid useless memory consumption
Library.prototype[name] = function() {
if (pluginInstance == null) pluginInstance = new plugin();
return pluginInstance; };
}
That's an interesting question. There was a blog post about prototype-liked development and the fact that a lot of people are avoiding it. I'll go with something like this:
var Library = function() {
var api;
var private = "some value";
var privateMethod = function() {
console.log(private);
}
var registerPlugin = function(name, plugin) {
api[name] = plugin.call(api);
}
var publicMethod = function() {
privateMethod();
}
return api = {
show: publicMethod,
plugin: registerPlugin
}
}
// usage of the library
var library = new Library();
library.show();
// registering a plugin
library.plugin("awesome", function() {
var api, library = this;
var pluginVar = "That's a plugin";
var pluginMethod = function() {
console.log(pluginVar);
library.show();
}
return api = {
gogo: pluginMethod
}
});
// calling a method of the plugin
library.awesome.gogo();
The library is just a function which has its own scope, its own private and public methods and exports an API. The plugin is actually another function with the same capabilities, but it is invoked with the library's API as a scope. So, all the public methods of the library are available and you are able to use them. And of course you keep the privacy of the plugin. I'll suggest to read about revealing module pattern. I personally use it alot. It really saves me a lot of problems.
P.S.
Here is a jsfiddle using the code above http://jsfiddle.net/XyTJF/

Object with nested array of objects and respective functions

I am trying to create an object with an array of objects.
Eg: I would like to create a Page object with multiple headers.
I need an addHeader() and getHeaders() method. A header itself is an object with few properties (eg name and id). I've been trying to nest it inside the page object as its part and add it to an array of Headers (which I assumed would be a part of Page, but that may not be the ideal solution).
I run into trouble when I tried to execute the inner functions.
I read this question and few similar on the topic, unfortunately I can't come up with a solution:
No ways to have class-based objects in javascript?
My most up to date attempt:
var Page = ( function () {
function Page() {
var headers = [];
var header = {
name:'',
id:'',
addHeader : function(name,id) {
this.name = name;
this.id = id;
Page.headers.push(header);
}
};
this.getHeaders = function() {
for(var i=0; i< Page.headers.length; i++) {
console.log(Page.headers[i]);
}
}
}
return Page;
}) ();
When I'm trying to run getHeaders:
var p = new Page(); p.getHeaders();
I get:
p.getHeaders is not a function
My javascript skills are rather basic, I would appreciate any help or comments.
Thank you for your answers.
Could someone explain to me how can I access the inner addHeader function?
Is this the correct way of nesting functions in javascript objects?
The way you have it, getHeaders is private to the scope it's in. To make it public, change it to:
this.getHeaders = function() {
// implementation
};
EDIT
For your addHeader comment - if that's supposed to be a public function, then just do the same thing as getHeaders - a public function. You can skip the whole var header = {...}; thing - based on what you're saying, you don't need that at all. Your function can look something like:
var Page = (function () {
function Page() {
var headers = [];
this.getHeaders = function () {
for (var i = 0; i < headers.length; i++) {
console.log(headers[i]);
}
};
this.addHeader = function (name, id) {
headers.push({ name: name, id: id });
};
}
return Page;
})();
EDIT
The prototype stuff won't work since you're accessing a private member. You need to keep those functions inside, where they still have access to headers, or else make headers public. I have removed the prototype part of the answer.
You want something like so:
//create a page object 'class'
function Page(){
this.headers = [];
};
//add functions to the page object
Page.prototype.addHeader = function(header){ this.headers.push(header); }
Page.prototype.getHeaders = function(){ return this.headers; }
function Header(opt){
this.name = opt.name || ""; //default to empty string
this.id = opt.id || null; //default to null id
}
//use it like so
Header header1 = new Header("title1","title1ID");
Page firstPage = new Page();
firstPage.addHeader( header1 );

Categories

Resources