Javascript-OpenERP: Modify/replace POS template view - javascript

So basically, I'm trying to introduce modifications in OpenERP's POS inteface from version 6.1. I see that the layout of this view can be found at /static/src/xml/pos.xml. What I want is to modify this view from my own addon (thus, not altering the original pos addon) and as far as I know, there is no way of inheriting this view to add changes (or is there?). So after studying the module, I'm trying to override its js function to slip in my own pos.xml with all my modifications (a copy of the original pos.xml, but with name 'PointOfSale_Mine' and other modifications). So far, I have added my own .js as follows:
openerp.my_pos = function(db) {
db.point_of_sale.PointOfSale = db.point_of_sale.PointOfSale.extend({
render: function() {
this._super.apply(this,arguments);
return qweb_template("PointOfSale_Mine")();
//return this._super.qweb_template("PointOfSale_Mine")();
//return db.point_of_sale.qweb_template("PointOfSale_Mine")();
}
})
};
And of course, I'm getting the error "qweb_template is not defined" as my JS skills and my knowledge regarding OpenERP6.1's new web framework is quite limited. I would really like to know how can I call the same method that the original 'render' function calls (You can see my useless attempts commented in the code above). Or is my whole approach wrong and there is a better way of introducing my changes to the template?
Thanks in advance. Any help will be appreciated.

Ok. After some trial and errors I came up with this code to do the trick:
openerp.my_pos = function(db) {
db.point_of_sale.PointOfSale = db.point_of_sale.PointOfSale.extend({
render: function() {
var rend = this._super();
var jdoc = $(rend);
jdoc.find('.pos-payment-container').prepend('<input type="text" value=""/>')
return jdoc[0].outerHTML;
}
})
};
It does not replace the entire pos.xml template as I was initially attempting, but it is probably better as you inherit the current template and introduce your modifications only (even if you have to .prepend() a big chunk of html code)

Related

Programmatically insert variables into Meteor Templates with Blaze.render + Blaze.view or Blaze.with

I'm trying to figure out how to programmatically insert a variable helper into a template using the Blaze API. I believe it needs to be done using some form of:
Blaze.render(Blaze.with('variable', contentFunction);
However, I'm struggling to get the contentFunction to work properly. I tried a function that returns a Session variable:
var session_var = function() {
return Session.get('myVariable');
};
However, I keep getting undefined errors. I know this could be done if I defined a separate template to be rendered, but that seems a bit much for this particular case. I was wondering if someone could explain how Blaze.with and Blaze.view work. Also, is it possible to programmatically insert just a variable helper into a template?
Without knowing exactly what you're trying to do, it's not clear if this is the easiest way of going about things.
However, if you look at the docs, you'll see that you can either supply the session variable in a data context and use Blaze.With, or supply no data context and use Blaze.View instead (which appears to be what you're going for).
So, if you want to render a reactive data context, it has to be supplied in the first argument to Blaze.With. Try something like this:
var myView = Blaze.With(
function() {
return {
foo: Session.get('foo')
};
},
function() {
return Spacebars.mustache([
'the value of foo is ',
this.lookup('foo')
]);
}
);
Blaze.render(myView, document.body); // or whatever element you want to render it inside
Alternatively, for Blaze.View:
var myView = Blaze.View(
function() {
return 'the value of foo is ' + Session.get('foo');
}
);
Blaze.render(myView, document.body);
To be honest, the easiest way to learn about the structure of renderable content (which is the difficult bit if you're doing this programmatically) is to study the stuff that the Spacebars compiler churns out. In other words, find an interesting template/sub-template that's been generated from quasi-HTML, pick an element out of it and run Blaze.getView([ELEMENT])._render, then study the result.

Best way to represent HTML part when writing a complex jQuery plugin

I have a complex jQuery plugin to write, It does have a lot of html to show on screen and I am supposed to create them. Well, what is the best way to do the job. I can already hard code them for sure , but is there any other elegant method? is there a way to use some kind of templating?. definitely I don't want to have lot more dependency either.
You can put them in an external file(s) and load them when you need but this means plugin users will have to download all your files, instead of a single js file. Another option I used before (not for a plugin though) is to have an object inside your plugin which holds all the html you want. This makes it easier to write the plugin as the html doesn't clutter the plugin code. Also when you need to edit the html, it's in a single place. You can also put your templating code inside this object.
var pi = function() {
var self = this;
self.getNewDiv = function(){
return repo.getDiv();
}
//..... all your plug in code goes here and at the bottom
var htmlRepository = function() {
var divCode = "<div></div>";
this.getDiv = function(){
return divCode;
};
this.getSpan = function(){
return "Span Content";
}
this.getDivWithSpan = function(){
return getSpan().wrap(divCode); //etc
}
};
var repo = new htmlRepository();
}

Adding Models to Backbone.js Collection Silently Fails

I am implementing a Blog Engine as a learning exercise for a new job. I have a Backbone.js Collection class called BlogList that is composed of BlogModel objects (a BlogModel is a single post to a blog). I have a masterBlogList that keeps all blog posts in memory for the lifetime of the application (I realize this is not a realistic design, but it is part of the spec).
I have chosen to use masterBlogList to hold the canonical state of the application. All new posts, edits, etc. are persisted to the database (MongoDB) as well as masterBlogList. When I want to display a subset of the posts in masterBlogList, I copy them into a new BlogList instance and then narrow this new instance down based on search criteria. Again, I realize this might not be the best design (cloning BlogModels and BlogLists), but it is what I've got and I'd prefer not to rework it.
The problem is that copying one BlogList to another is not working. Even when the source list is non-empty, the destination list always ends up being empty. I have tried to debug this every which way with no luck. Here is the relevant portion of the BlogList source code:
// BlogList
$ (function () {
App.BlogList = Backbone.Collection.extend ({
model : App.BlogModel,
url : '/blog-entries',
comparator : function (a) {
return -(new Date (a.get ('date')));
},
populateFromMemory : function (sourceList) {
// this.reset ();
var self = this;
sourceList.each (function (postModel) {
self.add(postModel);
});
var foo = new App.BlogModel();
this.add(foo);
},
(continued...)
Even the last bit regarding foo is not working. I've also tried adding a clone() of postModel and also new App.BlogModel(postModel.toJSON()).
Any help would be extremely appreciated!
Sorry to have bothered anyone :<, but I got it working. The code actually does work as written above. The problem is that my search criteria were filtering out all of the posts, so I wasn't seeing anything. End of a long day! Thanks to those who tried to help me...

Auto-load/include for JavaScript

I have file called common.js and it's included in each page of my site using <script />.
It will grow fast as my sites functionality will grow (I hope; I imagine). :)
Lets example I have a jQuery event:
$('#that').click(function() {
one_of_many_functions($(this));
}
For the moment, I have that one_of_many_functions() in common.js.
Is it somehow possible that JavaScript automatically loads file one_of_many_functions.js when such function is called, but it doesn't exist? Like auto-loader. :)
The second option I see is to do something like:
$('#that').click(function() {
include('one_of_many_functions');
one_of_many_functions($(this));
}
That not so automatically, but still - includes wanted file.
Is any of this possible? Thanks in an advice! :)
It is not possible to directly auto-load external javascripts on demand. It is, however, possible to implement a dynamic inclusion mechanism similar to the second route you mentioned.
There are some challenges though. When you "include" a new external script, you aren't going to be able to immediately use the included functionality, you'll have to wait until the script loads. This means that you'll have to fragment your code somewhat, which means that you'll have to make some decisions about what should just be included in the core vs. what can be included on demand.
You'll need to set up a central object that keeps track of which assets are already loaded. Here's a quick mockup of that:
var assets = {
assets: {},
include: function (asset_name, callback) {
if (typeof callback != 'function')
callback = function () { return false; };
if (typeof this.assets[asset_name] != 'undefined' )
return callback();
var html_doc = document.getElementsByTagName('head')[0];
var st = document.createElement('script');
st.setAttribute('language', 'javascript');
st.setAttribute('type', 'text/javascript');
st.setAttribute('src', asset_name);
st.onload = function () { assets._script_loaded(asset_name, callback); };
html_doc.appendChild(st);
},
_script_loaded: function (asset_name, callback) {
this.assets[asset_name] = true;
callback();
}
};
assets.inlude('myfile.js', function () {
/* do stuff that depends on myfile.js */
});
Sure it's possible -- but this can become painful to manage. In order to implement something like this, you're going to have to maintain an index of functions and their corresponding source file. As your project grows, this can be troublesome for a few reasons -- the 2 that stick out in my mind are:
A) You have the added responsibility of maintaining your index object/lookup mechanism so that your scripts know where to look when the function you're calling cannot be found.
B) This is one more thing that can go wrong when debugging your growing project.
I'm sure that someone else will mention this by the time I'm finished writing this, but your time would probably be better spent figuring out how to combine all of your code into a single .js file. The benefits to doing so are well-documented.
I have created something close to that a year ago. In fact, I have found this thread by search if that is something new on the field. You can see what I have created here: https://github.com/thiagomata/CanvasBox/blob/master/src/main/New.js
My project are, almost 100% OOP. So, I used this fact to focus my solution. I create this "Class" with the name "New" what is used to, first load and after instance the objects.
Here a example of someone using it:
var objSquare = New.Square(); // Square is loaded and after that instance is created
objSquare.x = objBox.width / 2;
objSquare.y = objBox.height / 2;
var objSomeExample = New.Stuff("some parameters can be sent too");
In this version I am not using some json with all js file position. The mapping is hardcore as you can see here:
New.prototype.arrMap = {
CanvasBox: "" + window.MAIN_PATH + "CanvasBox",
CanvasBoxBehavior: "" + window.MAIN_PATH + "CanvasBoxBehavior",
CanvasBoxButton: "" + window.MAIN_PATH + "CanvasBoxButton",
// (...)
};
But make this more automatic, using gulp or grunt is something what I am thinking to do, and it is not that hard.
This solution was created to be used into the project. So, the code may need some changes to be able to be used into any project. But may be a start.
Hope this helps.
As I said before, this still is a working progress. But I have created a more independent module what use gulp to keep it updated.
All the magic que be found in this links:
https://github.com/thiagomata/CanvasBox/blob/master/src/coffee/main/Instance.coffee
https://github.com/thiagomata/CanvasBox/blob/master/src/node/scripts.js
https://github.com/thiagomata/CanvasBox/blob/master/gulpfile.js
A special look should be in this lines of the Instance.coffee
###
# Create an instance of the object passing the argument
###
instaceObject = (->
ClassElement = (args) ->
window[args["0"]].apply this, args["1"]
->
ClassElement:: = (window[arguments["0"]])::
objElement = new ClassElement(arguments)
return objElement
)()
This lines allows me to initialize a instance of some object after load its file. As is used in the create method:
create:()->
#load()
return instaceObject(#packageName, arguments)

Javascript and jQuery file structure

I have created a sizable application javascript and jQuery. However my file structure is getting a bit messy!
At the moment I have one large JS file with a if ($('#myDiv').length > 0) { test at the top to only execute the code on the correct page, is this good practice?
There is also a mixture of plain JS functions and jQuery extensions in the same file e.g $.fn.myFunction = function(e) {.
I also have a few bits of code that look like this:
function Product() {
this.sku = '';
this.name = '';
this.price = '';
}
var myProduct = new Product;
Basket = new Object;
My question is for pointers on good practice regarding javascript and jQuery projects.
The code if ($('#myDiv').length > 0) { is not good practice. Instead, make your page specific JS as functions and execute them in the corresponding page . Like this:
var T01 = function(){
// JS specific to Template 01
};
var T02 = function(){
// JS specific to Template 02
};
HTML head of Template 01:
<script type="text/javascript"> $(T01); </script>
Consistency is the golden rule.
You can discuss design patterns back and forth, but if you want to have easily maintainable code where new people can come in and get an overview fairly quickly, the most important part, whatever design patterns you chose, is to have a consistent code base.
It is also the hardest thing to do - keeping your codebase clean and consistent is probably the hardest thing you can do as a programmer, and especially as a team.
Of course the first tip I can give you is to separate the jQuery extensions in their own source files. You can always serve everything together with a minification tool, so you should not worry about performance.
About the code youo mention, it could be simplified to
var Product = {
sku: '',
name: '',
price: ''
}
var myProduct = objectCopy(Product);
var Basket = {};
provided you write a simple objectCopy function which loops through the object own properties and just copies them to a new object (you can make a shallow or a deep copy, according to your needs).
Finally, if you think your code is starting to get messy, you may want to learn some patterns to organize JS code, like the module pattern. Alternatively, if you are familiar with doing this on the backend, you may want to organize your application following the MVC pattern. personal advertisement - I have written myself a tiny library which helps organize your code in this fashion. There are also many other libraries for the same task, often adding other functionality as well.
If you follow the MVC pattern, your page will actually correspond to some action in some controller, and you could just start it with a call like
<script>someController.someAction()</script>
in the head of your document, hence removing the need for the manual check for #myDiv. If you use my library MCV, it will be enough to declare your body like
<body class="mcv:controller/action">
and start the application with
$(document).ready(function() {
mcv.autostart();
});
Yes it's good practice to put as much of your code into a seperate JS file as this could then be compressed before transmission and hence speed up download time. However no you should not have code that looks like
if ($('#myDiv').length > 0) {
on every page. Split your JS code up into manageable functions and call those as-and-when you need to.
I don't see a problem with mixing JS and jQuery functions up in the same file.

Categories

Resources