How to override a JS variable in Odoo 11? - odoo

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.");
}
});

Related

Execute code after view update in vue.js

I want to perform a task (scrolling to bottom, since new elements where added) after a view has been updated by vue.
Here is my code:
export default {
mounted() {
this.connect();
},
connection: null,
methods: {
connect: function () {
...
},
onMessage: function (msg) {
this.messages.push(msg);
this.scrollDown();
return true;
},
scrollDown: function () {
$("html, body").animate({
'scrollTop': $(this).offset().top
}, 100);
}
As you can see, the this.scrollDown(); is invoked after this.messages.push(msg);, so since the view is not updated immediately the scroll is not well performed.
How is it supposed to be made?
Try waiting for the DOM to be updated using:
this.$nextTick(function () {
this.scrollDown();
});

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);
});

create an invisible widget that binds to keypressed

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);
});
},
});
};

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');

Sinon JS "Attempted to wrap ajax which is already wrapped"

I got the above error message when I ran my test. Below is my code (I'm using Backbone JS and Jasmine for testing). Does anyone know why this happens?
$(function() {
describe("Category", function() {
beforeEach(function() {
category = new Category;
sinon.spy(jQuery, "ajax");
}
it("should fetch notes", function() {
category.set({code: 123});
category.fetchNotes();
expect(category.trigger).toHaveBeenCalled();
}
})
}
You have to remove the spy after every test. Take a look at the example from the sinon docs:
{
setUp: function () {
sinon.spy(jQuery, "ajax");
},
tearDown: function () {
jQuery.ajax.restore(); // Unwraps the spy
},
"test should inspect jQuery.getJSON's usage of jQuery.ajax": function () {
jQuery.getJSON("/some/resource");
assert(jQuery.ajax.calledOnce);
assertEquals("/some/resource", jQuery.ajax.getCall(0).args[0].url);
assertEquals("json", jQuery.ajax.getCall(0).args[0].dataType);
}
}
So in your jasmine test should look like this:
$(function() {
describe("Category", function() {
beforeEach(function() {
category = new Category;
sinon.spy(jQuery, "ajax");
}
afterEach(function () {
jQuery.ajax.restore();
});
it("should fetch notes", function() {
category.set({code: 123});
category.fetchNotes();
expect(category.trigger).toHaveBeenCalled();
}
})
}
What you need in the very beginning is:
before ->
sandbox = sinon.sandbox.create()
afterEach ->
sandbox.restore()
Then call something like:
windowSpy = sandbox.spy windowService, 'scroll'
Please notice that I use coffee script.