in odoo 10, how do i modify the POS to add a new header widget? - python-2.7

Im trying to add a custom widget similar to HeaderButtonWidget, or StatusWidget, but im not able to do it yet:
my first attempt:
odoo.define('my_module.pos', function(require) {
"use strict";
var chrome = require('point_of_sale.chrome');
var _t = core._t;
var _lt = core._lt;
...
var AltHeader = chrome.HeaderButtonWidget.extend({
template : 'AltHeader',
init: function(parent, options){
options = options || {};
this._super(parent, options);
this.action = options.action;
this.label = options.label;
},
renderElement: function(){
var self = this;
this._super();
},
button_click: function(){ console.log("its a hit") },
highlight: function(highlight){
this.$el.toggleClass('highlight',!!highlight);
},
});
but this is not working, and HeaderButtonWidget is not returned by the main method of point_of_sale/static/src/js/screens.js so according to this page:
http://odoo-development.readthedocs.io/en/latest/dev/pos/gui.html so using the screen widget extend is not possible, how do i do this?
EDIT:
Here is what i have attempted:
odoo.define('pos_widget_toggler.pos', function (require) {
"use strict";
var PosBaseWidget = require('point_of_sale.BaseWidget');
var core = require('web.core');
var _t = core._t;
var _lt = core._lt;
var AltHeader = PosBaseWidget.extend({
template: 'AltHeader',
init: function(parent,options){
this._super(parent, options);
this.label = _lt('OFF');
this.action= function(){
// code will be here
} ;
},
});
return{
AltHeader:AltHeader
};
});
and pos_widget_toggler.xml
<templates id="pos_widget_toggler" inherit_id="point_of_sale.template" xml:space="preserve">
<t t-name="AltHeader">
<div class="oe_status">
<t t-esc="widget.label" />
</div>
</t>
</templates>
Im still not able to see anything,

To create new widget,
odoo.define('point_of_sale.chrome', function (require) {
"use strict";
var PosBaseWidget = require('point_of_sale.BaseWidget');
var AltHeader = PosBaseWidget.extend({
template: 'AltHeader',
init: function(parent, options){
//Your code
}
});
return{
AltHeader:AltHeader
};
});
your_custom_module/static/src/xml/file.xml
<t t-name="AltHeader">
<div class="oe_status">
// Your code
</div>
</t>
Add this template file in your manifest.
your_custom_module/__manifest__.py
'qweb': ['static/src/xml/pos.xml'],
Hope it will help you.

Related

BackboneJs unable to render template in rendered template

I have two views and I want to render a second one inside the first one.
I am doing this:
First view:
var EventContainer = Backbone.View.extend({
el: '#sectionContainer',
initialize: function() {
this.render();
},
render: function() {
var that = this;
$.get('templates/home/eventContainer.html', function(data) {
that.template = _.template(data, {}); //Option to pass any dynamic values to template
$(that.el).append(that.template); //adding the template content to the main template.
}, 'html');
new EventList();
return this;
}
});
Template for the first view:
<div class="main-content pull-left">
<ul id="eventList" class="event-list no-list-style no-padding"> </ul>
</div>
Second view:
var EventList = Backbone.View.extend({
el: '#eventList',
initialize: function() {
var that = this;
this.EventsCollection = new EventsCollection();
this.EventsCollection.fetch({
success: function(data) {
that.collection = data;
that.setTemplate();
},
error: function() {}
});
},
setTemplate: function() {
var that = this;
$.get('templates/home/eventList.html', function(data) {
that.template = _.template(data);
that.render();
}, 'html');
},
render: function() {
var that = this;
this.collection.each(function(eventData) {
var eventTemplate = that.template(eventData.toJSON());
console.log('---> ' + $(that.el));
$(that.el).append(eventTemplate);
});
return this;
}
});
Template for the second View:
<li class="event-item">
<small><%= like_count%> </small>
</li>
When I try to render the second view, el is undefined.
What m I doing wrong?
Thanks.
If you have a look at this.el inside EventList#initialize, I think you'll find that it is undefined rather than the <ul> that you're expecting it to be. Why would that be? Where does #eventList come from? #eventList comes from EventContainer's template and that won't be in the DOM until this AJAX call:
$.get('templates/home/eventContainer.html', ...)
completes. That $.get won't complete until well after your new EventList();. When you instantiate your EventList, it will go looking for #eventList but won't find anything, that will leave you with this.el being undefined. Then later on, that $.get will return and #eventList will show up in the DOM; this is why $('#eventList') works.
The easy solution is to defer instantiating your EventList until after you've added the necessary things to the DOM:
$.get('templates/home/eventContainer.html', function(data) {
that.template = _.template(data, {});
$(that.el).append(that.template);
new EventList(); // <-----------------------
}, 'html');
If you're going to do this, then you could make the dependency explicit by dropping the el from the EventList definition and specifying it when you create the view instead:
$.get('templates/home/eventContainer.html', function(data) {
that.template = _.template(data, {});
$(that.el).append(that.template);
new EventList({ el: that.$('#eventList') });
}, 'html');

Backbone - How to create a nested list with subview list items?

I'm a Backbone noob and I've been at a standstill for 2 days now and can't figure out where I'm going wrong. Could anyone help me out?
My app is retrieving a JSON file with a list of components in it. Each component has a category it belongs to. I create a view called "Components" that is a collapsible list. When a component category is clicked, it should open up to show the components in that category. Each of these components (list items) a separate view called "Component".
I'm using a lot of append()'s in the parent view and I don't think this is efficient. I tried to compile a string of html and then append it to the view in one statement but the events of the subviews weren't triggering.
There are probably a few errors going on here. Even though my sublist items should be wrapped in ul's they aren't being. If someone can put me on the path to enlightenment I'd be really grateful!
Here's my code
/* ----------------- PARENT VIEW ---------------------- */
var ComponentsView = Backbone.View.extend({
id: 'components-view',
className: 'components-view',
html: [
'<div class="panel panel--components">',
'<h3 class="panel__heading">add an item</h3>',
'<ul class="component-list"></ul>',
'</div>'
].join(''),
initialize: function(){
var types = [];
var currentTypeSelected = 1;
this.getTypes = function(){
return types;
}
this.getCurrentTypeSelected = function(){
return currentTypeSelected;
}
this.setCurrentTypeSelected = function(value){
currentTypeSelected = value;
}
if(this.collection.length){
this.collection.each(function(model){
var thisItemType = model.attributes.type;
if(types.indexOf(thisItemType)==-1){
types.push(thisItemType);
}
});
}
this.$el.html(this.html);
this.$componentList = this.$('.component-list');
this.render();
},
render: function(){
var that = this;
this.getTypes().forEach(function(type){
that.$('.component-list').append('<li class="component-type">' + type + '');
// now cycle through all the componenets of this type
that.$('.component-list').append('<ul>');
that.collection.byType(type).each(function(model){
that.$('.component-list').append('<li class="component">');
that.$('.component-list').append(that.renderIndividualComponent(model));
that.$('.component-list').append('</li>');
});
that.$('.component-list').append('</ul>');
});
},
renderIndividualComponent: function(model){
var componentView = new ComponentView({model: model});
return componentView.$el;
},
events: {
'click .component-type': 'onOpenSubList'
},
onOpenSubList: function (e) {
alert('open sub list');
}
});
/* ----------------- SUB (list item) VIEW ---------------------- */
var ComponentView = Backbone.View.extend({
tagName: "li",
className: "component",
initialize: function(model){
this.render();
},
render: function(){
var html = '' + this.model.attributes.description + ''//template(this.model.attributes);
$(this.el).append(html);
return this;
},
events: {
'click a': 'onAddComponent'
},
onAddComponent: function (e) {
e.preventDefault();
alert('add component');
}
});

KendoUI: Render template with external variable

I'm loading up some stuff via AJAX, and rendering it through kendo, but want to add something to it that's not in the AJAX-loaded data but set as a var in js, something like this:
<script id="some-template" type="text/x-kendo-template">
<div style="background-image: url('#= commonBackground #');">
<h2>#= title #</h2>
</div>
</script>
And then call this:
$('button').on('click', function(e){
e.preventDefault();
var commonBackground = '/images/background.png';
var someData = {
title: 'Lorem Ipsum'
};
var someTemplate = kendo.template($('some-template').html());
$('#target').append(someTemplate(someData));
});
Now I know this is horrible code, but I hope I'm getting the point across of what I'm trying to do here. I'm unsure how I should reference commonBackground from my template, any help with this? Documentation didn't make me any wiser...
Your variable commonBackground needs to be global.
Either define it as:
var commonBackground = '/images/background.png';
$(document).ready(function () {
...
});
or:
var commonBackground = undefined;
$(document).ready(function () {
...
$('button').on('click', function(e){
...
commonBackground = '/images/background.png';
...
});
});
IMPORTANT: Why is not commonBackground defined inside the template data as title is? I think that it is easier / cleaner doing:
$('#button').on('click', function (e) {
e.preventDefault();
var someData = {
title : 'Lorem Ipsum',
commonBackground: 'background.png'
};
var someTemplate = kendo.template($('#some-template').html());
$('#target').append(someTemplate(someData));
});

Ember.js: Upload file component

I need to create an Ember component to select a file.
My page will include multiple "upload component"
I have read a post trying to implement that: (https://stackoverflow.com/questions/9200000/file-upload-with-ember-data) BUT the UploadFileView is directly linked to the controller.
I would like to have something more generic...
I would like to remove the App.StoreCardController.set('logoFile'..) dependency from the view or pass the field (logoFile) from the template...
Any idea to improve this code ?
App.UploadFileView = Ember.TextField.extend({
type: 'file',
attributeBindings: ['name'],
change: function(evt) {
var self = this;
var input = evt.target;
if (input.files && input.files[0]) {
App.StoreCardController.set('logoFile', input.files[0]);
}
}
});
and the template:
{{view App.UploadFileView name="icon_image"}}
{{view App.UploadFileView name="logo_image"}}
I completed a full blown example to show this in action
https://github.com/toranb/ember-file-upload
Here is the basic handlebars template
<script type="text/x-handlebars" data-template-name="person">
{{view PersonApp.UploadFileView name="logo" contentBinding="content"}}
{{view PersonApp.UploadFileView name="other" contentBinding="content"}}
<a {{action submitFileUpload content target="parentView"}}>Save</a>
</script>
Here is the custom file view object
PersonApp.UploadFileView = Ember.TextField.extend({
type: 'file',
attributeBindings: ['name'],
change: function(evt) {
var self = this;
var input = evt.target;
if (input.files && input.files[0]) {
var reader = new FileReader();
var that = this;
reader.onload = function(e) {
var fileToUpload = reader.result;
self.get('controller').set(self.get('name'), fileToUpload);
}
reader.readAsDataURL(input.files[0]);
}
}
});
Here is the controller
PersonApp.PersonController = Ember.ObjectController.extend({
content: null,
logo: null,
other: null
});
And finally here is the view w/ submit event
PersonApp.PersonView = Ember.View.extend({
templateName: 'person',
submitFileUpload: function(event) {
event.preventDefault();
var person = PersonApp.Person.createRecord({ username: 'heyo', attachment: this.get('controller').get('logo'), other: this.get('controller').get('other') });
this.get('controller.target').get('store').commit();
}
});
This will drop 2 files on the file system if you spin up the django app
EDIT (2015.06): Just created a new solution based on a component.
This solution provides an upload button with a preview and remove icon.
P.S. The fa classes are Font Awesome
Component handlebars
<script type="text/x-handlebars" data-template-name='components/avatar-picker'>
{{#if content}}
<img src={{content}}/> <a {{action 'remove'}}><i class="fa fa-close"></i></a>
{{else}}
<i class="fa fa-picture-o"></i>
{{/if}}
{{input-image fdata=content}}
</script>
Component JavaScript
App.AvatarPickerComponent = Ember.Component.extend({
actions: {
remove: function() {
this.set("content", null);
}
}
});
App.InputImageComponent = Ember.TextField.extend({
type: 'file',
change: function (evt) {
var input = evt.target;
if (input.files && input.files[0]) {
var that = this;
var reader = new FileReader();
reader.onload = function (e) {
var data = e.target.result;
that.set('fdata', data);
};
reader.readAsDataURL(input.files[0]);
}
}
});
Usage example
{{avatar-picker content=model.avatar}}
Old Answer
I took Chris Meyers example, and I made it small.
Template
{{#view Ember.View contentBinding="foto"}}
{{view App.FotoUp}}
{{view App.FotoPreview width="200" srcBinding="foto"}}
{{/view}}
JavaScript
App.FotoPreview= Ember.View.extend({
attributeBindings: ['src'],
tagName: 'img',
});
App.FotoUp= Ember.TextField.extend({
type: 'file',
change: function(evt) {
var input = evt.target;
if (input.files && input.files[0]) {
var that = this;
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result;
that.set('parentView.content', data);
}
reader.readAsDataURL(input.files[0]);
}
},
});
Marek Fajkus you cannot use JQuery's .serialize, it makes no mention of file uploads in the documentation at JQuery UI docs
However, you could use JQuery Upload Plugin
Actually it does mention it, it says:
". Data from file select elements is not serialized."
In case of uploading multiple files, you may want to use
{{input type='file' multiple='true' valueBinding='file'}}
^^^^
This is a solution that you would use in normal HTML upload.
Additionally, you can use 'valueBinding' which will allow you to set up an observer against that value in your component.

Dojo Widget Templates

With reference to Simple Login implementation for Dojo MVC / - there is one point i don't understand. With regards to sample from phusick, the login dialog class does a call of dom.byId("dialog-template") - "dialog-template" is an id from the script which is the template for the dialog and should be present in an html template - not in the main html. So if I remove that, the call to dom.byId would fail
so my code structure is as follows
main.html ( calls Only main.js is called - nothing more)
main.js ( Contains the following)
require([
"dojo/_base/declare","dojo/_base/lang","dojo/on","dojo/dom","dojo/Evented",
"dojo/_base/Deferred","dojo/json","dijit/_Widget","dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin","dijit/Dialog",
"widgets/LoginDialog",
"widgets/LoginController",
"dijit/form/Form","dijit/form/ValidationTextBox","dijit/form/Button",
"dojo/domReady!"
], function(
declare,lang,on,dom,Evented,Deferred,JSON,
_Widget,
_TemplatedMixin,
_WidgetsInTemplateMixin,
Dialog,
LoginDialog,
LoginController
) {
// provide username & password in constructor
// since we do not have web service here to authenticate against
var loginController = new LoginController({username: "user", password: "user"});
var loginDialog = new LoginDialog({ controller: loginController});
loginDialog.startup();
loginDialog.show();
loginDialog.on("cancel", function() {
console.log("Login cancelled.");
});
loginDialog.on("error", function() {
console.log("Login error.");
});
loginDialog.on("success", function() {
console.log("Login success.");
console.log(JSON.stringify(this.form.get("value")));
});
});
Now LoginDialog.js and LoginDialogTemplate.html is the templatised widget for the dialog
and LoginController.js is the controller.
My LoginDialog.js is
define([
"dojo/_base/declare","dojo/_base/lang","dojo/on","dojo/dom","dojo/Evented","dojo/_base/Deferred","dojo/json",
"dijit/_Widget","dijit/_TemplatedMixin","dijit/_WidgetsInTemplateMixin",
"dijit/Dialog","dijit/form/Form","dijit/form/ValidationTextBox","dijit/form/Button",
"dojo/text!templates/loginDialogTemplate.html",
"dojo/text!templates/loginFormTemplate.html",
"dojo/domReady!"
], function(
declare,lang,on,dom,Evented,Deferred,JSON,
_Widget,
_TemplatedMixin,
_WidgetsInTemplateMixin,
Dialog,
Form,
Button,
template
) {
return declare([ Dialog, Evented], {
READY: 0,
BUSY: 1,
title: "Login Dialog",
message: "",
busyLabel: "Working...",
// Binding property values to DOM nodes in templates
// see: http://www.enterprisedojo.com/2010/10/02/lessons-in-widgetry-binding-property-values-to-dom-nodes-in-templates/
attributeMap: lang.delegate(dijit._Widget.prototype.attributeMap, {
message: {
node: "messageNode",
type: "innerHTML"
}
}),
constructor: function(/*Object*/ kwArgs) {
lang.mixin(this, kwArgs);
var dialogTemplate = dom.byId("dialog-template").textContent;
var formTemplate = dom.byId("login-form-template").textContent;
var template = lang.replace(dialogTemplate, {
form: formTemplate
});
var contentWidget = new (declare(
[_Widget, _TemplatedMixin, _WidgetsInTemplateMixin],
{
templateString: template
}
));
contentWidget.startup();
var content = this.content = contentWidget;
this.form = content.form;
// shortcuts
this.submitButton = content.submitButton;
this.cancelButton = content.cancelButton;
this.messageNode = content.messageNode;
},
postCreate: function() {
this.inherited(arguments);
this.readyState= this.READY;
this.okLabel = this.submitButton.get("label");
this.connect(this.submitButton, "onClick", "onSubmit");
this.connect(this.cancelButton, "onClick", "onCancel");
this.watch("readyState", lang.hitch(this, "_onReadyStateChange"));
this.form.watch("state", lang.hitch(this, "_onValidStateChange"));
this._onValidStateChange();
},
onSubmit: function() {
this.set("readyState", this.BUSY);
this.set("message", "");
var data = this.form.get("value");
var auth = this.controller.login(data);
Deferred.when(auth, lang.hitch(this, function(loginSuccess) {
if (loginSuccess === true) {
this.onLoginSuccess();
return;
}
this.onLoginError();
}));
},
onLoginSuccess: function() {
this.set("readyState", this.READY);
this.set("message", "Login sucessful.");
this.emit("success");
},
onLoginError: function() {
this.set("readyState", this.READY);
this.set("message", "Please try again.");
this.emit("error");
},
onCancel: function() {
this.emit("cancel");
},
_onValidStateChange: function() {
this.submitButton.set("disabled", !!this.form.get("state").length);
},
_onReadyStateChange: function() {
var isBusy = this.get("readyState") == this.BUSY;
this.submitButton.set("label", isBusy ? this.busyLabel : this.okLabel);
this.submitButton.set("disabled", isBusy);
}
});
});
My loginDialogTemplate.html is as follows
<script type="text/template" id="dialog-template">
<div style="width:300px;">
<div class="dijitDialogPaneContentArea">
<div data-dojo-attach-point="contentNode">
{form}
</div>
</div>
<div class="dijitDialogPaneActionBar">
<div
class="message"
data-dojo-attach-point="messageNode"
></div>
<button
data-dojo-type="dijit.form.Button"
data-dojo-props=""
data-dojo-attach-point="submitButton"
>
OK
</button>
<button
data-dojo-type="dijit.form.Button"
data-dojo-attach-point="cancelButton"
>
Cancel
</button>
</div>
</div>
</script>
Since the template has the id="dialog-template" so I guess when the widget calls the dom.byId("dialog-template"), it throws an error "TypeError: dom.byId(...) is null" at the line :-> var dialogTemplate = dom.byId("dialog-template").textContent;
So what am I doing wrong here?
If i use all the template scripts in the main html it works fine.
Asif,
Since you're passing in the templates in the define function, you don't need the dom.byId() to get the content. Try this:
Remove the elements from your HTML templates.
In LoginDialog.js, change your function arguments to:
...
Button,
dialogTemplate,
formTemplate
You'll need the formTemplate for the next change. I used 'dialogTemplate' instead of your 'template' so it's more obvious how it's replacing the code from the example. Next, change the beginning of the constructor to:
constructor: function(/*Object*/ kwArgs) {
lang.mixin(this, kwArgs);
//var dialogTemplate = dom.byId("dialog-template").textContent;
//var formTemplate = dom.byId("login-form-template").textContent;
var template = lang.replace(dialogTemplate, {
form: formTemplate
});
var contentWidget = new (declare(
...
I only left the commented code in so you can see what I changed. What it does is create a new template string called 'template' by substituting the {form} placeholder in your dialogTemplate HTML with the formTemplate you passed in. Then it's using that new template string to create the widget.