Mithril redraw only 1 module - mithril.js

If I have like 10 m.module on my page, can I call m.startComputation, m.endComputation, m.redraw or m.request for only one of those modules?
It looks like any of these will redraw all of my modules.
I know only module 1 will be affected by some piece of code, I only want mithril to redraw that.

Right now, there's no simple support for multi-tenancy (i.e. running multiple modules independently of each other).
The workaround would involve using subtree directives to prevent redraws on other modules, e.g.
//helpers
var target
function tenant(id, module) {
return {
controller: module.controller,
view: function(ctrl) {
return target == id ? module.view(ctrl) : {subtree: "retain"}
}
}
}
function local(id, callback) {
return function(e) {
target = id
callback.call(this, e)
}
}
//a module
var MyModule = {
controller: function() {
this.doStuff = function() {alert(1)}
},
view: function() {
return m("button[type=button]", {
onclick: local("MyModule", ctrl.doStuff)
}, "redraw only MyModule")
}
}
//init
m.module(element, tenant("MyModule", MyModule))
You could probably also use something like this or this to automate decorating of event handlers w/ local

Need multi-tenancy support for components ? Here is my gist link
var z = (function (){
//helpers
var cache = {};
var target;
var type = {}.toString;
var tenant=function(componentName, component) {
return {
controller: component.controller,
view: function(ctrl) {
var args=[];
if (arguments.length > 1) args = args.concat([].slice.call(arguments, 1))
if((type.call(target) === '[object Array]' && target.indexOf(componentName) > -1) || target === componentName || target === "all")
return component.view.apply(component, args.length ? [ctrl].concat(args) : [ctrl])
else
return {subtree: "retain"}
}
}
}
return {
withTarget:function(components, callback) {
return function(e) {
target = components;
callback.call(this, e)
}
},
component:function(componentName,component){
//target = componentName;
var args=[];
if (arguments.length > 2) args = args.concat([].slice.call(arguments, 2))
return m.component.apply(undefined,[tenant(componentName,component)].concat(args));
},
setTarget:function(targets){
target = targets;
},
bindOnce:function(componentName,viewName,view) {
if(cache[componentName] === undefined) {
cache[componentName] = {};
}
if (cache[componentName][viewName] === undefined) {
cache[componentName][viewName] = true
return view()
}
else return {subtree: "retain"}
},
removeCache:function(componentName){
delete cache[componentName]
}
}
})();

Related

I am writing a client side code to retreive a record but facing the below issue

I am writing this code using Xrm.Webapi.RetreiveRecord as below but I am getting the below error when debugging.
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
if (typeof (ContosoPermit) == "undefined") { var ContosoPermit = { __namespace: true }; }
if (typeof (ContosoPermit.Scripts) == "undefined") { ContosoPermit.Scripts = { __namespace: true }; }
ContosoPermit.Scripts.PermitForm = {
handleOnLoad: function (executionContext) {
console.log('on load - permit form');
},
handleOnChangePermitType: function (executionContext)
{
console.log('on change - permit type');
},
_handlePermitTypeSettings: function (executionContext) {
var formContext = executionContext.getFormContext();
var permitType = formContext.getAttribute("contoso_permittype").getValue();
if (permitType == null) {
formContext.ui.tabs.get("inspectionsTab").setVisible(false);
return;
} else {
var permitTypeID = permitType[0].id;
debugger;
Xrm.WebApi.retrieveRecord("contoso_permittype", permitTypeID).then(
function success(result) {
if (result.contoso_requireinspections) {
formContext.ui.tabs.get("inspectionstab").setVisible(true);
}
else {
formContext.ui.tabs.get("inspectionstab").setVisible(false);
}
},
function (error) { alert('Error' + error.message) });
}
},
__namespace: true
}

Custom directive to check the value of two or more fields

I tried my best to write a custom directive in Apollo Server Express to validate two input type fields.
But the code even works but the recording of the mutation already occurs.
I appreciate if anyone can help me fix any error in the code below.
This is just sample code, I need to test the value in two fields at the same time.
const { SchemaDirectiveVisitor } = require('apollo-server');
const { GraphQLScalarType, GraphQLNonNull, defaultFieldResolver } = require('graphql');
class RegexDirective extends SchemaDirectiveVisitor {
visitInputFieldDefinition(field) {
this.wrapType(field);
}
visitFieldDefinition(field) {
this.wrapType(field);
}
wrapType(field) {
const { resolve = defaultFieldResolver } = field;
field.resolve = async function (source, args, context, info) {
if (info.operation.operation === 'mutation') {
if (source[field.name] === 'error') {
throw new Error(`Find error: ${field.name}`);
}
}
return await resolve.call(this, source, args, context, info);
};
if (
field.type instanceof GraphQLNonNull
&& field.type.ofType instanceof GraphQLScalarType
) {
field.type = new GraphQLNonNull(
new RegexType(field.type.ofType),
);
} else if (field.type instanceof GraphQLScalarType) {
field.type = new RegexType(field.type);
} else {
// throw new Error(`Not a scalar type: ${field.type}`);
}
}
}
class RegexType extends GraphQLScalarType {
constructor(type) {
super({
name: 'RegexScalar',
serialize(value) {
return type.serialize(value);
},
parseValue(value) {
return type.parseValue(value);
},
parseLiteral(ast) {
const result = type.parseLiteral(ast);
return result;
},
});
}
}
module.exports = RegexDirective;

How can I seperate functions which are basically the same but use different states? Vue2/Vuex

Ive got a problem since i realized that I break the DRY(Dont repeat yourself) rule. So basically I have 2 modules(movies, cinemas) and few methods in them which look the same but use their module' state.
Example: Movies has 'movies' state. Cinemas has 'cinemas' state.
//cinemas.ts
#Mutation
deleteCinemaFromStore(id: string): void {
const cinemaIndex = this.cinemas.findIndex((item) => item.id === id);
if (cinemaIndex >= 0) {
const cinemasCopy = this.cinemas.map((obj) => {
return { ...obj };
});
cinemasCopy.splice(cinemaIndex, 1);
this.cinemas = cinemasCopy;
} else {
throw new Error("Provided id doesn't exist");
}
}
//movies.ts
#Mutation
deleteMovieFromStore(id: string): void {
const movieIndex = this.movies.findIndex((item) => item.id === id);
if (movieIndex >= 0) {
const moviesCopy = this.movies.map((obj) => {
return { ...obj };
});
moviesCopy.splice(movieIndex, 1);
this.movies = moviesCopy;
} else {
throw new Error("Provided id doesn't exist");
}
}
My struggle is: How can I seperate these methods into utils.ts if they have reference to 2 different states?
define another function that take the id, state and store context (this) as parameters :
function delete(id:string,itemsName:string,self:any){
const itemIndex= self[itemsName].findIndex((item) => item.id === id);
if (itemIndex>= 0) {
const itemsCopy = self[itemsName].map((obj) => {
return { ...obj };
});
itemsCopy.splice(itemIndex, 1);
self[itemsName] = itemsCopy;
} else {
throw new Error("Provided id doesn't exist");
}
}
then use it like :
//cities.ts
#Mutation
deleteCityFromStore(id: string): void {
delete(id,'cities',this)
}
//countries.ts
#Mutation
deleteCountryFromStore(id: string): void {
delete(id,'countries',this)
}

How to update values in QWeb template in odoo dynamically?

I'm trying to develop barcode scanning module to make inventory transfers using barcode scanner.
I made QWeb template and render it with this.$el.html method and I can see that view rendered appropriately. The problem is that values I pass to the template is not updating with client actions. How do I make them dynamically changed when I change them in js script? Code goes here:
barcode-scanner.js
odoo.define('stock.barcode_scanner', function(require) {
'use sctrict';
var AbstractAction = require('web.AbstractAction');
var core = require('web.core');
var QWeb = core.qweb;
var _t = core._t;
var BarcodeAction = AbstractAction.extend({
start: function() {
var self = this;
self.$el.html(QWeb.render("BarcodeHandlerView", {widget: self}));
core.bus.on('barcode_scanned', this, this._onBarcodeScanned);
return this._super();
},
destroy: function () {
core.bus.off('barcode_scanned', this, this._onBarcodeScanned);
this._super();
},
_onBarcodeScanned: function(barcode) {
var self = this;
this._rpc({
model: 'stock.barcode.handler',
method: 'product_scan',
args: [barcode, ],
})
.then(function (result) {
if (result.action) {
var action = result.action;
if (action.type === 'source_location_set') {
self.sourceLocation = action.value;
} else if (action.type === 'product_added') {
if (self.productsList === undefined) {
self.productsList = [];
}
self.productsList.push(action.value);
} else if (action.type === 'destination_location_set') {
self.destionationLocation = action.value;
} else if (action.type === 'validation') {
self.sourceLocation = undefined;
self.productsList = undefined;
self.destinationLocation = undefined;
}
}
if (result.warning) {
self.do_warn(result.warning);
}
});
},
});
core.action_registry.add('stock_barcode_scanner', BarcodeAction);
return {
BarcodeAction: BarcodeAction,
};
});
transfers.xml
<?xml version="1.0" encoding="utf-8"?>
<template id="theme.tp_remove_button">
<t t-name="BarcodeHandlerView">
<div>Source Location: <t t-esc="widget.sourceLocation"/></div>
<div>Destination Location: <t t-esc="widget.destinationLocation"/></div>
</t>
</template>
I am sure that everything is set properly at __manifest__.py - I can see the view, but client action doesn't trigger page update - and client actions are running when I trigger them - I can see my warnings I return from python function. What am I doing wrong? Should I use some other approach to achieve that?
You must to create an action to catch the event when your element change.
odoo.define('stock.barcode_scanner', function(require) {
'use sctrict';
var AbstractAction = require('web.AbstractAction');
var core = require('web.core');
var QWeb = core.qweb;
var _t = core._t;
var BarcodeAction = AbstractAction.extend({
// ***** Add this to catch events on your template
events: {
'change #destination-location': '_onBarcodeScanned',
},
start: function() {
var self = this;
self.$el.html(QWeb.render("BarcodeHandlerView", {widget: self}));
core.bus.on('barcode_scanned', this, this._onBarcodeScanned);
return this._super();
},
destroy: function () {
core.bus.off('barcode_scanned', this, this._onBarcodeScanned);
this._super();
},
action_change_destination: function (newLocation = null) {
if(newLocation === null){
return null;
}else{
let self = this;
console.log("My new awesome destination changing....");
self.destinationLocation = newLocation;
$("#detination-location").html(self.destinationLocation);
}
},
_onBarcodeScanned: function(barcode) {
var self = this;
this._rpc({
model: 'stock.barcode.handler',
method: 'product_scan',
args: [barcode, ],
})
.then(function (result) {
if (result.action) {
var action = result.action;
if (action.type === 'source_location_set') {
self.sourceLocation = action.value;
} else if (action.type === 'product_added') {
if (self.productsList === undefined) {
self.productsList = [];
}
self.productsList.push(action.value);
} else if (action.type === 'destination_location_set') {
self.destionationLocation = action.value;
// ****** GO AND CHANGE YOUR DESTINATION *****
self.action_change_destination(self.destionationLocation);
} else if (action.type === 'validation') {
self.sourceLocation = undefined;
self.productsList = undefined;
self.destinationLocation = undefined;
}
}
if (result.warning) {
self.do_warn(result.warning);
}
});
},
});
core.action_registry.add('stock_barcode_scanner', BarcodeAction);
return {
BarcodeAction: BarcodeAction,
};
});
And in your view should be like this:
<?xml version="1.0" encoding="utf-8"?>
<template id="theme.tp_remove_button">
<t t-name="BarcodeHandlerView">
<div>Source Location: <t t-esc="widget.sourceLocation"/></div>
<div>Destination Location: <span id="destination-location" /></div>
</t>
</template>

YouTube OnStateChange with multiple players on the same page

I have multiple YouTube players on a single page inside a banner slider and I want to use the YouTube Player API to control them and do other stuff based on the state of the video's. I have the code below which I'm pretty sure of used to work fine where any state changes where registered. But it doesnt work for me anymore. The YouTube object is still there and I can use it to start and stop a video but the onStateChange event never gets triggered. What is wrong with this code?
var YT_ready = (function() {
var onReady_funcs = [],
api_isReady = false;
return function(func, b_before) {
if (func === true) {
api_isReady = true;
for (var i = 0; i < onReady_funcs.length; i++) {
onReady_funcs.shift()();
}
}
else if (typeof func == "function") {
if (api_isReady) func();
else onReady_funcs[b_before ? "unshift" : "push"](func);
}
}
})();
function onYouTubePlayerAPIReady() {
YT_ready(true);
}
var players_homepage = {};
YT_ready(function() {
$("li.video iframe.youtube").each(function(event) {
var frameID_homepage = $(this).attr('id');
if (frameID_homepage) {
players_homepage[frameID_homepage] = new YT.Player(frameID_homepage, {
events: {
'onStateChange': onPlayerStateChange_homepage
}
});
}
});
});
(function(){
var tag = document.createElement('script');
tag.src = "//www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
})();
function onPlayerStateChange_homepage(event) {
if (event.data === 0) {
// do something on end
} else if (event.data === 1) {
// do something on play
} else if (event.data === 2) {
// do something on pause
}
}
function pauseVideo_homepage(previousVideo) {
players_homepage[previousVideo].pauseVideo();
}
function playVideo_homepage(currentVideo) {
players_homepage[currentVideo].playVideo();
}