create an invisible widget that binds to keypressed - odoo

I want to create an invisible widget that binds to keypressed anywhere on the webclient after the login.
I have tried to extent Widget
but as i don't need to render anything , i don't know how to put it in the html on screen.
should i try to extent some other class in OpenErp to get this working?
console.log('start') // Custome message to dispaly on console
openerp.web_hello = function(instance) {
console.log('Object',instance);
var invisibleWidget = instance.web.Widget.extend({
template: 'invisibleWidget',
init: function(parent){
this._super(parent);
},
});
};

Following javascript module is a good start for the solution. Just sharing for others who are interested
/*---------------------------------------------------------
* OpenERP module
*---------------------------------------------------------*/
openerp.web_keypressed = function(instance) {
instance.web.InvisibleWidget = instance.web.Widget.extend({
template: 'InvisibleWidget',
init: function() {
this._super.apply(this, arguments);
instance.web.InvisibleWidget = this;
},
start: function() {
this._super.apply(this, arguments);
},
sessKeyPressed: function(ed, e) {
console.log('sessKeyPressed');
},
});
instance.web.WebClient = instance.web.WebClient.extend({
show_application: function() {
this._super.apply(this, arguments);
var self = this;
console.log('webclient extend');
self.invisible = new instance.web.InvisibleWidget(this);
self.invisible.appendTo(self.$el);
console.log(instance.web.InvisibleWidget);
$(document).bind('keypress.session', function (ed, e) {
instance.web.InvisibleWidget.sessKeyPressed(ed, e);
});
},
});
};

Related

How to override a JS variable in Odoo 11?

Consider the below class exists
local.MyWidget = instance.Widget.extend({
events: {
"click .my_button": "button_clicked",
},
button_clicked: function() {
console.log("Button Clicked");
}
});
I want to add one more event to the variable like below
MyWidget.include({
events: {
"click .new_button": "new_button_clicked",
},
new_button_clicked: function() {
console.log("New Button Clicked.");
}
});
The above doesn't work and the error i get is
events is undefined
How this can be done in odoo?
You can do it like this as well:
MyWidget.include({
init: function(parent, options) {
this.events["click .new_button"] = "new_button_clicked";
this._super(parent, options);
},
new_button_clicked: function() {
console.log("New Button Clicked.");
}
});

Show popover if hovering an element longer

Using Bootstrap popover to show a content of a hidden div as popover content
How would one implement not showing the popover until the main element is not hovered for at least one second?
$(function () {
$("[data-toggle=popover]").popover({
trigger: "manual",
html: true,
content: function () {
var content = $(this).attr("data-popover-content");
return $(content).children(".popover-body").html();
},
title: function () {
var title = $(this).attr("data-popover-content");
return $(title).children(".popover-heading").html();
}
})
.on("mouseenter", function () {
var _this = this;
$(_this).popover("show");
$(".popover").on("mouseleave", function () {
setTimeout(function () {
if (!$(".popover:hover").length) {
$(_this).popover("hide");
}
}, 300);
});
})
.on("mouseleave", function () {
var _this = this;
setTimeout(function () {
if (!$(".popover:hover").length) {
$(_this).popover("hide");
}
}, 300);
});});
I have this page with a table containing a lot of icons. Each icon has some data that is kind of big and so was moved to scrollable popovers. I want the popowers to be shown, but nost so much that you move the mouse across the page and they all light up. That is why I need the delay before they appear. The delay after mouse leave is so the popovers don't close when i want to enter them and scroll their content.
I change my code to open them on click until i get another solution.
Fiddle: https://jsfiddle.net/mandor/v5e73fzu/14/
Use a flag variable and check it/set it with some setTimeouts...
var timerReady = false
var showPopup;
$("[data-toggle=popover]").popover({
trigger: "manual",
html: true,
content: function() {
var content = $(this).attr("data-popover-content");
return $(content).children(".popover-body").html();
},
title: function() {
var title = $(this).attr("data-popover-content");
return $(title).children(".popover-heading").html();
}
})
.on("mouseenter", function() {
var _this = this;
timerReady = true
showPopup = setTimeout(function(){
if(timerReady){
$(_this).popover("show");
}
}, 1000)
})
.on("mouseleave", function() {
clearTimeout(showPopup)
timerReady = false
var _this = this;
setTimeout(function() {
if (!$(".popover:hover").length) {
$(_this).popover("hide");
}
}, 300);
});

Fail to mock with Intern more than once

I have followed the guide from Sitepen to mock AMD modules (LINK), but when I try to mock the same Module twice, the mocking fails, telling me my mock has no constructor. It crashes in the AMD-Mocker here:
try {
originalModule = require(moduleId); [...]
}
Has anybody ever stumbled across this issue and has found a solution for it? It does not help to create two test files, either.
Here is my compressed code:
define(function (require) {
var registerSuite = require('intern!object');
var assert = require('intern/chai!assert');
var amdMocker = require('support/AMDMocker');
registerSuite( function(){
var Hello;
return{
before: function(){
return amdMocker.mock('amd/Hello', {
'amd/Resolver': 'support/ResolverMocked'
}).then(function (mocked) {
Hello = mocked;
});
},
name: 'helloMocked',
'greet': function () {
var hello = new Hello();
}
}
});
registerSuite( function(){
var Hello2;
return{
before: function(){
return amdMocker.mock('amd/Hello', {
'amd/Resolver': 'support/ResolverMocked'
}).then(function (mocked) {
Hello2 = mocked;
});
},
name: 'helloMocked2',
'greet': function () {
var hello = new Hello2(); // here it crashes
}
}
});
});

Handle methods differently in Vue depending on mobile or not

I'm having trouble setting up my Vue components to handle their methods differently if the user is on mobile. For instance a navigation drop down, if a user clicks on a link, I want to prevent them from going to that location, but instead drop down the drop down. Whereas on desktop, I want them to go to it if they click on it and only drop down on hover. I'll need this for so many other aspects of my project.
I have a main Vue instance:
var Main = new Vue({
el: 'body',
data: {
mobile: true
},
ready: function() {
if( document.clientWidth >= 992 )
{
this.mobile = false;
}
}
});
export default Main;
Then for my components, I'm doing something like this:
import Main from './../Main';
var NavLink = Vue.component('navlink', {
template: '#nav-link-template',
replace: true,
data: function() {
return {
}
},
props: ['text', 'url'],
ready: function() {
},
methods: {
handleClick: function(e) {
e.preventDefault();
console.log(Main.mobile);
if( Main.mobile )
{
if( this.$children.length )
{
// Has dropdown
this.$children[0].dropDown();
}
else
{
// No dropdown so redirect user
window.location = this.url;
}
}
else
{
// Not mobile so let user go
window.location = this.url;
}
}
}
});
Not only does Main.mobile return the default value no matter what resolution because their ready methods seem to run BEFORE the Main ready method.. but this also feels like the wrong setup.
Thanks for any insight.
First, according to you code, you dont need Main commonjs module to be a vuejs instance. Make it as a simple js object
Main = {
mobule: document.clientWidth >= 992
}
export default Main;
Or you may want to handle client window size dynamically
var Main = new Vue({
created: function() {
// dunno why v-on="resize: func" not working
global.window.addEventListener('resize', function () {
//calc width and heigh somehow
self.$broadcast('resize', width, heigh);
});
}
});
export default Main;

subscribed events in durandaljs are not unsubscribed

This is the flow of the application:
The shell.js loads the schoolyeardialog.js which contains the schoolyearbrowser.js which loads the schoolyearwizard.js via create or edit button.
When I repeat these steps multiple times: click create/edit button then I do multiple
requests in my SchoolyearWizard to this:
$.when(service.editSchoolyear(schoolyearId))
The reason is that the subscribed events are not correctly unsubscribed in my opinion.
I have tried different ways of unsubscribing in the SchoolyearDialog.js file.
Either the events did not fire - when I did the subscription.off(); after the app.on(...)
or It was unsubscribed at the wrong position.
Where should I unsubscribe correctly?
If you guys need a sample repo as visual studio solution I can provide this if it helps or maybe you see clearly and immediately the error?!
I have also thought about unsubscribing from the 2 events create/edit when the SchoolyearDialog module is "unloaded" because then both events could/would be unsubscribed not only the create OR edit subscription as it is now when I either click the add or edit button... how would I do that?
SHELL
define(['plugins/router', 'durandal/app', 'viewmodels/SchoolyearDialog', 'knockout'], function (router, app, schoolyearDialog, ko) {
self.schoolyearIsLoaded = ko.observable(false);
var saveTimeTableSubscription = app.on('savedTimeTable').then(function (options) {
// after coming the 2nd time here
if (!self.schoolyearIsLoaded()) {
router.map([{ route: 'lessonplanner', moduleId: 'viewmodels/lessonplanner', title: 'lesson planner', nav: true },
{ route: 'documentbrowser', moduleId: 'viewmodels/documentbrowser', title: 'document browser', nav: true }])
.buildNavigationModel();
self.schoolyearIsLoaded(true);
}
router.navigate("lessonplanner", true);
});
return {
router: router,
activate: function () {
router.map([{ route: '', moduleId: 'viewmodels/SchoolyearDialog', nav: true, title: 'Schoolyearbrowser' }
]).buildNavigationModel();
return router.activate('SchoolyearDialog');
}
};
});
SchoolyearDialog
define(['durandal/app', 'knockout', 'plugins/router', 'viewmodels/SchoolyearWizard'],
function (app, ko, router, wizard) {
var ctor = function () {
debugger;
var self = this;
self.createSubscribe = ko.observable();
self.editSubscribe = ko.observable();
self.activeScreen = ko.observable('viewmodels/SchoolyearBrowser'); // set the schoolyear browser as default module
var createWizardSubscription = app.on('createWizard').then(function () {
self.createSubscribe().off();
self.createSubscribe(null);
self.activeScreen(new wizard('create'));
}, self);
self.createSubscribe(createWizardSubscription);
var editWizardSubscription = app.on('editWizard').then(function (schoolyearId) {
self.editSubscribe().off();
self.editSubscribe(null);
self.activeScreen(new wizard('edit', schoolyearId));
}, self);
self.editSubscribe(editWizardSubscription);
}
return ctor;
});
SchoolyearBrowser
define(['durandal/app', 'plugins/dialog', 'knockout', 'services/dataservice', 'plugins/router'],
function (app, dialog, ko, dataservice, router) {
var SchoolyearBrowser = function () {
var self = this;
self.schoolyears = ko.observableArray();
$.when(dataservice.getSchoolyears())
.done(function (schoolyearModels) {
self.schoolyears(schoolyearModels);
});
self.create = function () {
app.trigger('createWizard');
}
self.edit = function () {
app.trigger('editWizard', 1);
}
};
return SchoolyearBrowser;
});
SchoolyearWizard
define(['durandal/activator', 'viewmodels/step1', 'viewmodels/step2', 'knockout', 'durandal/app', 'services/dataservice', 'viewmodels/CreateEditSchoolyearViewModel'],
function (activator, Step1, Step2, ko, app, service, CreateEditSchoolyearViewModel) {
var ctor = function (viewMode, schoolyearId) {
debugger;
// depending on the mode I could setup 2 different step modules for create and edit ? and the Wizard has one property called content
if (viewMode === 'edit') {
$.when(service.editSchoolyear(schoolyearId))
.done(function (response) {
debugger;
self.viewModel(new CreateEditSchoolyearViewModel(response));
}).fail(function (error) {
alert(error);
});
}
else if (viewMode === 'create') {
$.when(service.createSchoolyear())
.done(function (response) {
debugger;
self.viewModel(new CreateEditSchoolyearViewModel(response));
}).fail(function (error) {
alert(error);
});
}
var self = this;
var steps = [new Step1(viewMode), new Step2(viewMode)];
var step = ko.observable(0); // Start with first step
self.activeStep = activator.create();
var stepsLength = steps.length;
self.viewModel = ko.observable();
this.hasPrevious = ko.computed(function () {
return step() > 0;
});
self.caption = ko.observable();
this.activeStep(steps[step()]);
this.hasNext = ko.computed(function () {
if ((step() === stepsLength - 1) && self.activeStep().isValid()) {
// save
self.caption('save');
return true;
} else if ((step() < stepsLength - 1) && self.activeStep().isValid()) {
self.caption('next');
return true;
}
});
this.isLastStep = function () {
return step() === stepsLength - 1;
}
this.next = function () {
if (this.isLastStep()) {
var vm = this.activeStep(); //.viewModel;
$.when(service.saveCreateSchoolyear({ schoolyearId: 1 })).done(function () {
app.trigger('savedTimeTable', { isSuccess: true });
}).fail(function (e) {
alert(e);
});
}
else if (step() < stepsLength) {
step(step() + 1);
self.activeStep(steps[step()]);
}
}
this.previous = function () {
if (step() > 0) {
step(step() - 1);
self.activeStep(steps[step()]);
}
}
}
return ctor;
});
This helped me greatly:
activator.deactivate function allows the previous object to execute custom deactivation logic."
SchoolyearDialog.js
self.deactivate = function () {
self.createSubscribe().off();
self.editSubscribe().off();
}
When the schoolyearDialog is deactivated both events are unsubscribed independing wether button create/edit is clicked. This is for me a clean solution :)
I agree with your solution but I'd recommend not to use plain .off() without parameters as this will cause to deregister all the events in the application.
Rather pass the event name as a parameter to your off method:
self.createSubscribe().off('savedTimeTable');