I want to override the class Activity in activity js file (mail module):
odoo.define('mail/static/src/models/activity/activity/js', function (require) {
'use strict';
const { registerNewModel } = require('mail/static/src/model/model_core.js');
const { attr, many2many, many2one } = require('mail/static/src/model/model_field.js');
const { clear } = require('mail/static/src/model/model_field_command.js');
function factory(dependencies) {
class Activity extends dependencies['mail.model'] {
............
Thanks for advice.
You will need to use the registerInstancePatchModel function.
At the bottom of activity.js, you will see:
registerNewModel('mail.activity', factory);
Then in your js file, if you wanted to override the _computeNote function:
import { registerInstancePatchModel } from '#mail/model/model_core';
registerInstancePatchModel('mail.activity', 'your_module_name', {
_computeNote() {
this._super.apply(this, arguments); // if you want to call super
// your code...
},
});
Related
I want to change the title of odoo in the browser tag, but for some reason, I cannot change the source code.
I found the code where to set the title in webclient.js but I don't know how to use it in my code.
/** #odoo-module **/
import { ActionContainer } from "./actions/action_container";
import { NavBar } from "./navbar/navbar";
import { useBus, useEffect, useService } from "#web/core/utils/hooks";
import { useTooltip } from "#web/core/tooltip/tooltip_hook";
import { NotUpdatable } from "../core/utils/components";
import { MainComponentsContainer } from "../core/main_components_container";
import { useOwnDebugContext } from "../core/debug/debug_context";
import { registry } from "#web/core/registry";
import { DebugMenu } from "#web/core/debug/debug_menu";
import { localization } from "#web/core/l10n/localization";
const { Component, hooks } = owl;
const { useExternalListener } = hooks;
export class WebClient extends Component {
setup() {
this.menuService = useService("menu");
this.actionService = useService("action");
this.title = useService("title");
this.router = useService("router");
this.user = useService("user");
useService("legacy_service_provider");
useOwnDebugContext({ categories: ["default"] });
if (this.env.debug) {
registry.category("systray").add(
"web.debug_mode_menu",
{
Component: DebugMenu,
},
{ sequence: 100 }
);
}
this.localization = localization;
this.title.setParts({ zopenerp: "Odoo" }); // zopenerp is easy to grep
useBus(this.env.bus, "ROUTE_CHANGE", this.loadRouterState);
useBus(this.env.bus, "ACTION_MANAGER:UI-UPDATED", (mode) => {
if (mode !== "new") {
this.el.classList.toggle("o_fullscreen", mode === "fullscreen");
}
});
.......
You need to patch the web client component class method
when one wishes to patch a standard method from a class, then we actually need to patch the prototype
Example:
import { WebClient } from "#web/webclient/webclient"
import {patch} from "web.utils";
patch(WebClient.prototype, "title patch", {
setup() {
this._super();
this.title.setParts({ zopenerp: "" });
},
});
Attempting to create unit test with jest. I've tried us ts-jest, had lots of issues. Trying babel, getting much closer. Actually got A test to work. But then I ran into this.
static cachedVendors = [];
^
SyntaxError: Unexpected token =
File looks as follows
//imports up here somewhere
export default class Vendor {
public static cachedVendors: Vendor[] = [];
...more code
}
jest config
const { defaults: tsjPreset } = require("ts-jest/presets");
module.exports = {
...tsjPreset,
transform: {
"^.+\\.[t|j]s?$": "babel-jest",
".*\\.(vue)$": "vue-jest"
},
moduleNameMapper: {
"^#/(.*)$": "<rootDir>/src/$1",
"^common/(.*)$": "<rootDir>/src/common/$1",
"^price/(.*)$": "<rootDir>/src/price/$1",
"^eligibility/(.*)$": "<rootDir>/src/eligibility/$1"
}
};
I have removed the ...tsjPreset stuff and get the same error.
My babel.config.js
module.exports = {
presets: [["#babel/preset-env", { targets: { node: "current" } }], "#babel/preset-typescript"],
plugins: [["#babel/plugin-proposal-export-default-from"]]
};
As I said, I have another file I'm running test on that looks like this
import Vue from "vue";
import { Component, Prop, Inject, Watch } from "vue-property-decorator";
import moment from "moment";
import * as _ from "lodash";
export const DATE_FORMAT = "MM-DD-YYYY";
#Component({
})
export default class PEMDatePicker extends Vue {
#Prop()
label: string;
#Prop()
value: string;
#Prop()
rules: Array<Function>; //https://vuetifyjs.com/en/components/forms#creating-rules
public componentsLabel = "";
public componentsDate = "";
public showPicker = false;
public componentsDateInput: string = moment().format("MM/DD/YYYY");
public mounted() {
this.componentsLabel = this.label ? this.label : "Date";
this.setDate(this.value);
this.$emit('input', this.value);
}
#Watch("value")
public syncModel() {
if (this.componentsDate != this.value) {
this.setDate(this.value);
}
this.componentsDateInput = this.getDate;
}
#Watch("componentsDate")
public syncDates() {
this.componentsDateInput = this.getDate;
this.$emit('input', this.componentsDate);
}
get getDate() {
return this.componentsDate ? moment(this.componentsDate).format("MM/DD/YYYY") : "";
}
public closePicker() {
this.showPicker = false;
//#ts-ignore
this.$refs.input.focus();
}
public handleEnterKey(event) {
this.setDate(event.target.value);
this.showPicker = false;
}
setDate(dateIn) {
this.componentsDate = dateIn && moment(dateIn).isValid() ? moment(dateIn).format("YYYY-MM-DD") : null;
}
}
This test on this file run successfully. So it's transpiling the typescript. But not on that specific file for some reason. I am at a loss.
Babel is a bit conservative about new js language features (especially if you're not using the newest version of babel). In this case, you appear to need this babel plugin:
https://babeljs.io/docs/en/babel-plugin-proposal-class-properties
I wonder how it is possible to use RESTDataSource in type-graphql and thus cache correctly in a redis. I would be grateful for a small example.
At the moment I use the DI container to get a service, which is extended from the RestDataSource class, but this is not the right way.
BookmarkResolver.ts
import { Resolver, FieldResolver, Root, Query, Ctx, Authorized } from 'type-graphql';
import { DealService } from '../service/DealService';
import { AvailableLocale } from '../enum/AvailableLocale';
import { Bookmark } from '../entity/Bookmark';
#Resolver(_of => Bookmark)
export class BookmarkResolver {
constructor(private dealService: DealService) {}
#FieldResolver()
async wordpressDeal(#Root() bookmark: Bookmark) {
return await this.dealService.getDealById(bookmark.item_id, AvailableLocale.STAGING);
}
}
DealService.ts
import { Service } from 'typedi';
import { AbstractService } from './AbstractService';
import { AvailableLocale } from '../enum/AvailableLocale';
#Service()
export class DealService extends AbstractService {
baseURL = process.env.DEAL_SERVICE_URL;
async getDealById(dealId: string | number, locale: AvailableLocale) {
const response = await this.get(
'deals/' + dealId,
{ locale }
);
return this.dealReducer(response);
}
dealReducer(deal: any) {
return {
id: deal.id || 0,
title: deal.title
};
}
}
AbstractService.ts
import { RESTDataSource, HTTPCache } from 'apollo-datasource-rest';
import { Service } from 'typedi';
#Service()
export class AbstractService extends RESTDataSource {
constructor() {
super();
this.httpCache = new HTTPCache();
}
}
Share the RESTDataSource via ApolloServer's context. Use it in the resolver by accessing the context with the #Ctx() decorator.
1. Define a RESTDataSource
Define the data source according to the apollo-datasource-rest example.
export class TodoDataSource extends RESTDataSource {
constructor() {
super();
this.baseURL = "https://jsonplaceholder.typicode.com/todos";
}
async getTodos(): Promise<Todo[]> {
return this.get("/");
}
}
2. Create an instance of the DataSource and put it in the Context
When you start the server, add data sources to the context by defining a function that creates the data sources.
const server = new ApolloServer({
schema,
playground: true,
dataSources: () => ({
todoDataSource: new TodoDataSource(),
}),
});
3. Access the DataSource in the resolver
Use the #Ctx() decorator to access the context in the resolver so you can use the data source.
#Resolver(Todo)
export class TodoResolver {
#Query(() => [Todo])
async todos(#Ctx() context: Context) {
return context.dataSources.todoDataSource.getTodos();
}
}
Full, runnable example at https://github.com/lauriharpf/type-graphql-restdatasource
Trying to build my own form validation plugin (it's for learning purposes - so I don't use existing libraries).
So I created the following mixin:
export default {
beforeCreate() {
if (! this.$vnode || /^(keep-alive|transition|transition-group)$/.test(this.$vnode.tag)) {
return;
}
// create
this.$validator = new Instance();
// define computed
if (! this.$options.computed) {
this.$options.computed = {};
}
this.$options.computed['errors'] = function() {
return this.$validator.errors;
};
}
}
And loaded the mixin from the component (cause I don't want to see this anywhere):
export default {
name: "SignIn",
components: {
AppLayout,
TextField,
HelperText,
Button
},
mixins: [ValidateMixin]
}
Anyway, anytime input has changed - there is an event which tests the value and controls my errors bag:
export default class {
constructor() {
this.items = {};
}
first(name) {
if (name in this.items) {
return this.items[name][0];
}
return false;
}
add(name, errors) {
this.items[name] = errors;
}
remove(name) {
delete this.items[name];
}
has(name) {
return name in this.items;
}
all() {
return this.items;
}
}
I've bind HTML element (:invalid="errors.has('email')"), and with the devtools I can see the errors bag changing - but the binding is just doesn't work. The invalid property remains false no matter what I'm doing.
I do understand that in order to create reactive property, I've to handle this with getters/setters, but I'm a bit stuck with it.
I'm trying to figure out how to use dynamic import in webpack with mithril. To do that elegantly, I think I'll need to use an async function somewhere along the line. Right now this is how I have used the async function:
import m from 'mithril'
let App = async () => {
let { Component } = await import('./components.js')
return {
view () {
return m(Component)
}
}
}
App().then(app => m.mount(document.body, app))
Ideally, I want to use it like this:
import m from 'mithril'
let App = {
async view () {
let { Component } = await import('./components.js')
return m(Component)
}
}
}
m.mount(document.body, App)
Is there something I've been missing from the documentation to acheive what I'd like to do? I've tried to look at every mention of promise, but it's possible that I've missed this.
Any help would be appreciated.
One way that should work is this:
async function main() {
const myModule = await import('./myModule.js');
const {export1, export2} = await import('./myModule.js');
const [module1, module2, module3] =
await Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
]);
}
main();
(async () => {
const myModule = await import('./myModule.js');
})();
For further information follow the link below.
ES proposal: import() – dynamically importing ES modules
Try the following, which provides a simple component named DynamicComponent which can be used anywhere and with children:
App.js
import m from 'mithril'
import { DynamicComponent } from './DynamicComponent'
const App = {
view() {
return m( DynamicComponent, {
component: 'OtherComponent'
}, 'Hello world' ),
}
}
}
m.mount(document.body, App)
OtherComponent.js
import m from 'mithril'
export function OtherComponent() { return {
view({ children }) { return m( 'div', children )}
}}
DynamicComponent.js
import { hooks } from '/hooks'
export function DynamicComponent() { return {
...hooks,
attrs: null,
component: null,
view({ children }) { return (
// Await module resolution and process attributes.
// Use '&&' as a shortcut to only continue
// once 'this.component' isn't null.
// Pass a clone of attrs to the loaded component.
this.component && m( this.component.default, this.attrs, children )
)}
}}
hooks.js
async function oninit({ attrs }) {
// Preload -> Load immediately, in parallel
// Prefetch -> Load when browser is idle (Can be less responsive)
// See more: https://webpack.js.org/guides/code-splitting/#prefetching-preloading-modules
// Dynamically import component and append '.js' (Don't include '.js' in your imports).
if ( attrs.loadType = 'prefetch' ) {
// Lazy load
this.component = await import( /* webpackPrefetch: true */ `
${ attrs.component }.js`
)
} else {
// Immediate load
this.component = await import( /* webpackPreload: true */ `
${ attrs.component }.js`
)
}
/*
Process and pass along attributes
This clones the attributes to prevent any changes from affecting
the original attributes.
You can save memory if it becomes a problem by directly
assigning `v.attrs` to `newAttrs`, but you lose this immutability.
*/
const newAttrs = { ...v.attrs }
// Remove attributes used in `DynamicComponent`
delete newAttrs.component
delete newAttrs.loadType
// Assign to component
this.attrs = newAttrs
m.redraw()
}
export const hooks = {
oninit,
}