Create a widget in odoo 10 for pos - odoo

I trying to create a custom widget,
*.js
odoo.define('pos_widget',function (require) {
var PosBaseWidget = require('point_of_sale.BaseWidget');
aleert('Alert One');//It alerts
var NewWidget = PosBaseWidget.extend({
template: 'NewWidget',
init: function(parent,options){
alert('Alert Two inside init function'); // It not alerts
var self = this;
},
});
});
But getting an error on console:
Error: Service pos_widget already defined boot.js:119:27
No type for action Object { context: Object } action_manager.js:631:13
error: Some modules could not be started
Failed modules: Array [ "point_of_sale.chrome" ]
Non loaded modules: Array [ "point_of_sale.main" ]
Debug: Object { point_of_sale.main: Object, point_of_sale.chrome: Object }
Note
I have added these lines in chrome.js file (point_of_sale module) directly, and works. But not in custom module.
How can i resolve this?

It seems that the name you have used is conflict 'pos_widget'
Change it to with your something like modulename.pos_custom_widget

Check path of your js file given in your xml file. It should be like this way:
XML file :
<?xml version="1.0" encoding="utf-8"?>
<template id="assets" inherit_id="point_of_sale.assets">
<xpath expr="." position="inside">
<script type="text/javascript" src="/custom_module/static/src/js/js_file.js"></script>
</xpath>
</template>
After that for js file it should be like this:
JS file:
odoo.define('custom_module.file_name', function (require) {
"use strict";
var PosBaseWidget = require('point_of_sale.BaseWidget');
var TableWidget = PosBaseWidget.extend({
template: 'TableWidget',
init: function(parent, options){
this._super(parent, options);
alert("Custom Widget");
}
});
});
After this add your xml file in manifest like this:
Manifest file:
'data': [
'views/pos_restaurant_views.xml',
],
Also after this you have to create your qweb template in xml file. And add this qweb temlpate in manifest like this:
qweb template in manifest
'qweb': [
'static/src/xml/qweb_file.xml',
],
After this run your POS in front.

Related

Custom widget js doesn't recognize template from qweb

I try to test custom widget from js reference and I get error in debugger:
Error: QWeb2: Template 'some.template' not found
qweb.xml was properly set in manifest, because when I extend ListController and use another template, it works correctly.
Here is template definition, which I use in qweb.xml:
<?xml version="1.0" encoding="UTF-8"?>
<template>
<div t-name="some.template">
<span class="val"><t t-esc="widget.count"/></span>
<button>Increment</button>
</div>
</template>
I tried to change <template> -> <templates>, totally removed tag "template" but still get the same error message.
JS:
odoo.define('working.test', function (require) {
var Widget = require('web.Widget');
var Counter = Widget.extend({
template: 'some.template',
events: {
'click button': '_onClick',
},
init: function (parent, value) {
this._super(parent);
this.count = value;
},
_onClick: function () {
this.count++;
this.$('.val').text(this.count);
},
});
// Create the instance
var counter = new Counter(this, 4);
// Render and insert into DOM
counter.appendTo(".o_nocontent_help");
})
Manifest:
# -*- coding: utf-8 -*-
{
'name': "testwidget",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "http://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/12.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base'],
'qweb': ['static/qweb.xml'],
# always loaded
'data': [
# 'security/ir.model.access.csv',
'views/views.xml',
'views/web_asset.xml',
],
# only loaded in demonstration mode
'demo': [
'demo/demo.xml',
],
}
Any idea how I need to modify this template to make the widget working correctly and in which table in db odoo stores these templates?
I was running into this same issue and needed to put my QWeb code into static/src/xml/base.xml in order for Odoo to recognize it.
You can check to see if Odoo is loading the QWeb by going to this URL on your Odoo instance:
<odoo_instance>/web/webclient/qweb?mods=<my_module_name>
Such as:
localhost:8069/web/webclient/qweb?mods=test
For comparison, you can see a successful output by using mods=web to load the QWeb assets for the web module.
You can try changing
'qweb': ['static/qweb.xml'],
to
'qweb': ['static/*.xml'],
It happens with me sometimes, by specifying static xml file name, it does not render that template. But by just loading all .xml files by using *, templates are loaded.
To solve this issue I used as workaround Widget.xmlDependencies:
xmlDependencies: ['/test/static/qweb.xml']
but the main reason I think was cache in PyCharm which I didn't invalidate.
After having done some code reading, IMO, I realized the official documentation might not have pointed out clearly how to use templates in frontend.
To summarize my understanding:
The 'qweb' field in manifest is mainly designed for webclient (i.e. the backoffice), not the website. When entering webclient, a request to /web/webclient/qweb is made to retrieve all the templates of installed modules.
In order to use templates in website (i.e. frontend), synchronous and asynchronous ways both exist.
Synchronous way: Use qweb.add_template. When parameter is template content itself or a DOM node, template is loaded in a synchronous way. (While param is a URL, then it fires up an ajax request to server to fetch content.)
qweb.add_template is mentioned in https://www.odoo.com/documentation/13.0/reference/qweb.html
Asynchronous way:
Use ajax.loadXML which you can use anywhere you want to start loading template from a URL.
Use xmlDependencies which you specify in widget definition. And if you dig into the code in widget.js, you can see ajax.loadXML is being used in willStart.
There are discussions regarding qweb.add_template vs ajax.loadXML
See https://github.com/OCA/pylint-odoo/issues/186 and https://github.com/odoo/odoo/issues/20821
FYI.
I guess you may need to make sure that the js definition refers to the module name correctly
odoo.define('MODULE TECHNICAL NAME SHOULD BE HERE.test', function (require) {});
you should also register your js function with something like:
core.action_registry.add("module_name.name", Widget_Extend);
for more info https://www.odoo.com/documentation/11.0/reference/javascript_reference.html#registries
In Odoo 14 make sure
dashboard.js
odoo.define('library_managment.dashboard', function(require) {
"use strict";
// alert("hello odoo...............")
console.log("Hello My Module........!!")
var widgetRegistry = require('web.widget_registry');
var Widget = require('web.Widget');
var Counter = Widget.extend({
template: 'library_managment.template',
xmlDependencies: ['/library_managment/static/src/xml/template.xml'],
events: {
'click button': '_onClick',
},
init: function (parent, value) {
this._super(parent);
this.count = 4*9+5;
console.log("parent is", parent)
console.log("counter is..", this.count)
},
_onClick: function () {
this.count++;
this.$('.val').text(this.count);
},
});
widgetRegistry.add('library_counter', Counter);
return Counter;
});
template.xml
add this
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<div t-name="library_managment.template">
<span class="val">
<t t-esc="widget.count"/>
</span>
<button class="bg-danger">Increment</button>
</div>
</odoo>
then add js file in assets.xml inside youe views
<odoo>
<template id="assets_backend" name="Library assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/library_managment/static/src/js/dashboard.js"></script>
</xpath>
</template>
</odoo>
then add in manifest like this:
'js': ['/static/src/js/dashboard.js'],
'qweb': ['/static/src/xml/template.xml']
then inside form view add this line
<widget="library_counter"/>
I had the same problem but with "hr_org_chart" template idk why everything works fine in another computer but in mine it returned this problem, I solved it by installing this module hr-org-chart

Using <object> to embed svg but doesn't show anything

I was trying to use to embed the svg picture but it does not show anything. I looked at some other threads and it was suggested to add type="image/svg+xml", however, it did not solve the issue. When I am trying to look at the DOM for some reason it seems to create an endless loop. I attached the picture
This is the compononent
<template>
<div class="logo">
<object type="image/svg+xml" data="logo.svg">
</object>
</div>
</template>
This is the app.vue
template>
<div id="app">
<Demo></Demo>
</div>
</template>
<script>
import Demo from './components/Demo.vue'
export default {
name: 'app',
components: {
Demo
}
}
</script>
```[![Snapshot][1]][1]
[1]: https://i.stack.imgur.com/Q6ipO.png
This happen because vue-loader doesn’t recognize paths in just any attribute. By default just recognize these ones: https://vue-loader.vuejs.org/options.html#transformasseturls
So, there are 3 possible solutions
Note: If you are not using eslint as linter you could remove eslint comments
1: Bind the route to your image
First add the next variable to your data in the component
data() {
return {
// eslint-disable-next-line global-require
mySvg: require('../assets/logo.svg'),
};
},
Next modify your template
<object type="image/svg+xml" :data="mySvg">
2: Add vue-loader rule
If you don't want to have to bind every svg image, you could add a rule to vue-loader in order to say how to handle data attribute in a object
Go to your webpack config file, if you created the project using vue-cli 3.x you have to create a vue.config.js file in the root (same level that package.json)
// vue.config.js
module.exports = {
chainWebpack: (config) => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap((options) => {
// eslint-disable-next-line no-param-reassign
options.transformAssetUrls = {
object: 'data',
};
return options;
});
},
};
if you want to check that the config was added, execute vue inspect > webpack.config and expect see something like this (inside webpack.config):
{
loader: 'vue-loader',
options: {
...
transformAssetUrls: {
object: 'data'
}
}
}
More info: https://cli.vuejs.org/guide/webpack.html#working-with-webpack
3: Replace default loader and use svg as vue components
Other option is use vue-svg-loader. This loader inlines the SVGs which enables you to modify them using css. Also optimize your files with SVGO
See more: https://vue-svg-loader.js.org/#vue-cli
It is worth checking that you don't have a CSS rule hiding object tags. Otherwise it seems correct. You probably need to check the path and make sure you can reach your image. I assume your filename is a dummy, but try to use an absolute path. And make sure you can hit the path and see the image in your browser.

how to reference URL into Vue.Component.Template

How to refer URL into Vue.Template link.
Template is longer and all operations are going to include to mounted/methods.
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: './views/templatebutton.html' //how to refer URL here.
})
You could read the local HTML file as a string, and then load the result into the template field. With a module loader (such as Webpack), you would use require() to import the HTML file:
// Foo.js
Vue.component('button-counter', {
template: require('./views/templatebutton.html')
})
Alternatively, if vue-loader is available to your project, you could use single file components, which allow importing the template from an external file:
<!-- Foo.vue -->
<template src="./views/templatebutton.html" />
demo
I solve this limitation using requirejs (
although it is not recommended).
You can load the text from html file by adding 'text!' before the template url and load it as text like:
var template = require('text!/assets/vuejs/controllers/venda_direta/cart.html');
and then use it as your template string:
...
template : template
...

js file path issue YII2

I want to include my custom js file which is in C:\xampp\htdocs\yii2\vendor\bower\backend\assets\js but console gives me error
Failed to load resource: the server responded with a status of 404 (Not Found)
While other same file have same above directory which is working.
In my appAsset file is under C:\xampp\htdocs\yii2\backend\assets
<?php
namespace backend\assets;
use yii\web\AssetBundle;
/**
* Main backend application asset bundle.
*/
class AppAsset extends AssetBundle
{
//public $basePath = '#webroot';
//public $baseUrl = '#web';
public $sourcePath = '#bower/backend/';
public $css = [
'assets/css/chosen.css',
'assets/css/style.css',
'assets/css/font-awesome.min.css',
'assets/css/bootstrap.min.css',
//'assets/css/bootstrap.css',
'assets/css/jquery.dataTables.min.css',
'assets/css/w3.css',
'assets/css/jquery-ui.css',
];
public $js = [
//'assets/js/jquery.min.js',
'assets/js/jquery-ui.js',
'assets/js/jquery.dataTables.min.js',
'assets/js/jquery-ui.multidatespicker.js',
'assets/js/chosen.jquery.js',
'assets/js/chosen.jquery.js',
'assets/js/my-custom.js',
];
public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
];
}
and my-custom.js
<script>
$(document).ready(function(){
$('li.active .treeview').on('click', function(e) {
$('li.active .treeview-menu').toggleClass("hide");
e.preventDefault();
});
});
</script>
The url of the file which is not finding is this
http://localhost/yii2/backend/web/assets/c4875c89/assets/js/my-custom.js
Simply add this in view file
<?php
$this->registerJsFile('PATH_TO_FILE');
?>
Use RegisterJsFile concept:
You should simply register this js file in your view, e.g. :
$this->registerJsFile('#web/js/specific.js');
Or your custompath
$this->registerJsFile('PATH_TO_FILELOCATION');
Read more :http://www.yiiframework.com/doc-2.0/guide-output-client-scripts.html#registering-scripts
and
http://www.yiiframework.com/doc-2.0/yii-web-view.html#registerJsFile()-detail

understanding dojo AMD loading- functions are undefined

I have been trying to get someone to explain to me how the dojo AMD loading works and to get a simple piece of code to work. I understand that if using for example the CDN, one has to call the dojo library and load all modules you wish to use. I have tried to implement other javascript functions based on activity from the main page and I will always get the function either undefined or an error related to a dojo control undefined. It seems that all the modules that initially load are not available to the rest of the code. Any helpful explanations would be really appreciated.
<link rel="stylesheet" type=
"text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.4/dojo/resources
/dojo.css" />
<link rel="stylesheet" type=
"text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.4/dijit/themes/
tundra/tundra.css" />
<link rel="stylesheet" type=
"text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.4/dojox/mobile/themes/
iphone/iphone.css" />
<title> DOJO </title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.4/
dojo/dojo.js"
data-dojo-config="async:true"></script>
<script type="text/javascript" src="Scripts/login.js"></script>
<script type="text/javascript">
require(["dojox/mobile/parser",
"dojo/parser",
"dojo/on",
"dojo/request/xhr",
"dijit/form/Form",
"dojo/store/Observable",
"dojo/store/Memory",
"dijit/Toolbar",
"dijit/Dialog",
"dojo/io/script",
"dojo/query",
"dojo/_base/lang",
"dijit/layout/ContentPane",
"dojox/mobile/Button",
"dojox/mobile/deviceTheme",
"dojox/mobile/compat",
"dojox/mobile/Heading",
"dojox/mobile/TextBox",
"dojox/mobile/Opener",
"dijit/form/TextBox",
"dijit/form/HorizontalSlider",
"dijit/form/ValidationTextBox",
"dijit/Calendar",
"dojox/mobile/ScrollableView",
"dojo/dom",
"dojo/domReady!",
"dojox/mobile"],
function (dom, domReady ,mobile, ScrollableView,
parser, query, domClass, domStyle, on, event, xhr,Form,
lang, Button, deviceTheme, compat, Heading) {
dojox.mobile.parser.parse();
});
</script>
From my understanding is that the way I have the code above is that my interface will load correctly and all widgets in the body of html will be displayed and it works fine. The problem is that I have a form that gets input from the user and on a button click event calls a function that handles the webrequests. I could not get this to work and it is merely a problem with where I am placing this function. I have added a simplified version:
What I have done is add that function to a script file to separate it from the rest of the code:
var dojoXhr;
function correctInput(div, td, msg) {
dojo.domStyle.set(div, 'display', '');
td.innerHTML = msg;
}
require(["dojo/_base/declare", "dojo/parser", "dojo/query", "dojo/dom-class",
"dojo/dom-style", "dojo/on",
"dojo/_base/event",
"dojo/request/xhr", "dijit/form/ValidationTextBox", "dojo/domReady!"],
function chklogin(declare, parser, query, dom-class, dom-style,
on, event, xhr,ValidationTextBox, domReady) {
var lname = dijit.byId('login').get('value');
var psswd = dijit.byId('password').get('value');
var feedback = document.getElementById('feedback');
var feedbackTD = dojo.query('td.feedback')[0];
if (lname == '' || psswd == '') {
correctInput(feedback, feedbackTD, 'Please enter a valid login!');
dojo.domStyle.set(feedback, 'display', '');
dojo.domStyle.set(document.getElementById('msgBodyOutter'), 'display', 'none');
feedbackTD.innerHTML = "Please enter a valid login!";
return;
}
if (!(lname == 'login') || !(psswd == 'password')) {
correctInput(feedback, feedbackTD, 'Please enter a valid login!');
return;
}
else {
dojo.domStyle.set(feedback, 'display', '');
dojo.domStyle.set(document.getElementById('msgBodyOutter'), 'display', 'none');
feedbackTD.innerHTML = "THATS IT BRO!";
return;
}
});
I got advice on the dojo forum to put my function in a define function and then use a require to call it all. I could not figure out how to do this.
It seems that all the modules that initially load are not available to
the rest of the code.
You are using a CDN to load the dojo toolkit. When you use CDN you are required to define the location of the module packages. You need to edit the dojoConfig for the code to work.
See this article about Using Custom Modules with a CDN. The important part is the packages object.
<script data-dojo-config="async: 1, dojoBlankHtmlUrl: '/blank.html',
packages: [ {
name: 'custom',
location: location.pathname.replace(/\/[^/]+$/, '') + '/js/custom'
} ]"
src="//ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojo/dojo.js">
</script>
Edit: Below is a simple dojo application.
So in my case create a module called chklogin, then require it, and
when the user clicks the button it will call that module chklogin from
within the main require[] function. Correct?
I would say yes. You are correct. I think your concept is a viable option. I hope this example helps with implementing define() to create your own modules. I will try to help where I can as you develop your idea. You can download the project here while available.
Directory Structure:
/index.html
/js/config.js
/js/controller/Controller.js
/js/modules/MyFirstModule.js
/index.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo</title>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dijit/themes/claro/claro.css">
<script src="js/config.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojo/dojo.js"></script>
<script>
require(["app/Controller", "dojo/domReady!"], function(Controller) {
//Initiate the entire application by calling main method of our Controller class.
Controller.main();
//Call our getter method of the Controller class to show how to access a private variable.
console.log(Controller.getWelcomeMessage());
});
</script>
</head>
<body class="claro" id="appBody"></body>
</html>
/js/config.js
We use packages to reference the CDN dojo files. Now we can call dojo classes by our package name
For example, "dojo/domReady!", "dijit/form/Button", "dojox/app/main". The dojo files
are stored on the google servers, which is referenced by the
<script src='http://ajax.googleapis.com/ajax/libs/dojo/1.9.1/dojo/dojo.js'>< /script>
in the index.html file.
Here we create our own custom packages. This could be for your modules, widgets, etc. The package
locations will map to the javascript directory that you store your custom dojo files in.
For example, myModules can be found in the /js/modules directory. You will reference any custom
dojo files via "myModules/MyModule", which locates and loads "/myModules/MyModule.js" file.
For an explanation of the baseURL, see: http://dojotoolkit.org/documentation/tutorials/1.9/hello_dojo/
"Defining and Requiring Modules". This code registers the correct location of our own packages so
we can load Dojo from the CDN whilst still being able to load local modules.
I created a package called "app" as you can see below. This is how I initialize my app in my project.
This was designed to allow me to keep the separation of code the best I know how. It is loaded and
called in the index.html page. So i give it a package name of app. It is physically located in the
js/controller/Controller.js file.
This dojoConfig object is used in the index.html and must be loaded prior to < script src='...dojo.js' > tag.
var dojoConfig = {
async: true,
tlmSiblingOfDojo: false,
baseUrl: location.pathname.replace(/\/[^/]*$/, ''),
packages: [
{ name: "myModules", location: "js/modules" },
{ name: "app", location: "js/controller", main: "Controller" }
]
};
if you choose to host the dojo files on your own server, you can reference them like below. Assuming the dojo js files are located in the "/js/dojo/*" directory.
packages: [
{ name: "dojo", location: "dojo/dojo" },
{ name: "dijit", location: "dojo/dijit" },
{ name: "dojox", location: "dojo/dojox" },
{ name: "myModules", location: "js/modules" },
{ name: "app", location: "js/controller", main: "Controller" }
]
/js/controller/Controller.js
Here is the controller which I use to initialize the web app.
define(["myModules/MyFirstModule"], function(MyFirstModule) {
//Private Variables...
var privateVariable1 = "Welcome to my Dojo Application!";
var privateVariable2;
/**
* init. This is a private function that is only available within this object.
*/
init = function() {
// proceed directly with startup
console.log("Startup functions are firing...");
//Render our "form" which only contains a single text box.
renderForm();
},
renderForm = function() {
MyFirstModule.createForm("appBody");
}
/**
* Enclose all public methods in the return object
*/
return {
/**
* main. This is a public function that can be called from other code.
*/
main: function() {
//Run init() method.
init();
},
/**
* getWelcomeMessage. This public function returns the value of the privateVariable1.
* This mimics a getter method.
*/
getWelcomeMessage: function() {
return privateVariable1;
}
};
}); //end define
/js/modules/MyFirstModule.js
This is an example of a custom Module. It is required by the Controller class as a dependency.
define([
//The required dependencies for this module.
"dojo/dom", "dojo/on", "dijit/form/TextBox", "dijit/form/Button"
], function(dom, on, TextBox, Button){
// Once all modules in the dependency list have loaded, this
// function is called to define the myModules/myFirstModule module.
//
// The dojo/dom module is passed as the first argument to this
// function; additional modules in the dependency list would be
// passed in as subsequent arguments (on, TextBox, and Button).
// Private variables
var firstNameTextBox;
var submitButton;
privateFunction = function() {
console.log("I am a private function. I can only be called from this class.");
};
// This returned object becomes the defined value of this module when called elsewhere.
return {
/**
* createForm. This method creates a simple form. Textbox and button.
* #param placeMeHere This is where to place the form elements. In this demo, the are placed in the
* body of the html document. This is executed in the Controller class.
*/
createForm: function(placeMeHere) {
//Create new TextBox.
firstNameTextBox = new TextBox({
name: "firstname",
value: "" /* no or empty value! */,
placeHolder: "type in your name"
}, "firstname");
//Place me in the DOM.
firstNameTextBox.placeAt(placeMeHere);
//Render
firstNameTextBox.startup();
//Create Button
submitButton = new Button({
label: "Say Hi"
}, "submitButton");
submitButton.placeAt(placeMeHere);
submitButton.startup();
//Greet the user.
on(submitButton, "click", function(evt){
console.log("Hi there, " + firstNameTextBox.get("value"));
});
}
};
});