override pivot view _render function in custom module odoo 11 - pivot-table

I want to override "_render: function ()" function from pivot_renderer.js file in web but not working in custom module. Here is the code i am implementing in my custom module:-
odoo.define('MY_CUSTOM_MODULE_NAME.renderer', function (require) {
"use strict";
var PivotRenderer = require('web.PivotRenderer');
var field_utils = require('web.field_utils');
var core = require('web.core');
var _t = core._t;
PivotRenderer.include({
init: function(parent, state, params) {
this._super.apply(this, arguments);
},
_render: function () {
if (!this._hasContent()) {
// display the nocontent helper
this.replaceElement(QWeb.render('PivotView.nodata'));
return this._super.apply(this, arguments);
}
if (!this.$el.is('table')) {
// coming from the no content helper, so the root element has to be
// re-rendered before rendering and appending its content
this.renderElement();
}
var $fragment = $(document.createDocumentFragment());
var $table = $('<table>').appendTo($fragment);
var $thead = $('<thead>').appendTo($table).addClass("CLASS_NAME");
var $tbody = $('<tbody>').appendTo($table);
var nbr_measures = this.state.measures.length;
var nbrCols = (this.state.mainColWidth === 1) ?
nbr_measures :
(this.state.mainColWidth + 1) * nbr_measures;
for (var i=0; i < nbrCols + 1; i++) {
$table.prepend($('<col>'));
}
this._renderHeaders($thead, this.state.headers);
this._renderRows($tbody, this.state.rows);
// todo: make sure the next line does something
$table.find('.o_pivot_header_cell_opened,.o_pivot_header_cell_closed').tooltip();
this.$el.html($table.contents());
return this._super.apply(this, arguments);
},
});
});
In the above, i want to add a class in the header for calling my custom css "var $thead = $('').appendTo($table).addClass("CLASS_NAME");" with this syntax but it is not reflecting in my custom module. Although, for testing, I have implemented same class in default web module and it is working fine. The issue is in custom module.
So how to solve this issue? Is there any other way for calling class or i am doing it in a wrong way?

var $thead = $('').addClass("CLASS_NAME").appendTo($table);
This will work in my case. You can try it.

Related

What happens when I use angular.module(...).controller(...).directive(...)?And why? [duplicate]

This question already has answers here:
How to implement chained method calls like jQuery?
(4 answers)
Closed 5 years ago.
I think this equals to
var module = angular.module(...);
module.controller(...);
module.directive(...);
But I'm not sure. And I don't know what happens in angular and why I could write code this way.
I try to debug and trace it but it's so confused.
This is called a fluent API.
Each method will return the module instance, so that another method may be called.
To illustrate we can create a class that does something similar.
class Module {
controller() {
console.log('controller');
return this;
}
directive() {
console.log('directive');
return this;
}
}
When each method has finished, it will return the module instance this so that another method can be chained.
So now we can use this class and chain the methods like this:
new Module().controller().directive();
Or
const module = new Module();
module.controller();
module.directive();
What happens when I use angular.module(…).controller(…).directive(…)?And why?
Short answer
This is good way to write your code in one file.
If you want to split Angular project to different files, use 2nd approach:
var app = angular.module(...);
app.controller(...);
app.directive(...);
Long answer
Also take a look on this angular code snippets (took from https://code.angularjs.org/1.5.6/angular.js):
You can see controller, directive, module, filter, factory, value, provider, decorator, animation, config, component ,run returns moduleInstance
function setupModuleLoader(window) {
var $injectorMinErr = minErr('$injector');
var ngMinErr = minErr('ng');
function ensure(obj, name, factory) {
return obj[name] || (obj[name] = factory());
}
var angular = ensure(window, 'angular', Object);
// We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
angular.$$minErr = angular.$$minErr || minErr;
return ensure(angular, 'module', function() {
/** #type {Object.<string, angular.Module>} */
var modules = {};
return function module(name, requires, configFn) {
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
}
};
assertNotHasOwnProperty(name, 'module');
if (requires && modules.hasOwnProperty(name)) {
modules[name] = null;
}
return ensure(modules, name, function() {
if (!requires) {
throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
"the module name or forgot to load it. If registering a module ensure that you " +
"specify the dependencies as the second argument.", name);
}
/** #type {!Array.<Array.<*>>} */
var invokeQueue = [];
/** #type {!Array.<Function>} */
var configBlocks = [];
/** #type {!Array.<Function>} */
var runBlocks = [];
var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
/** #type {angular.Module} */
var moduleInstance = {
// Private state
_invokeQueue: invokeQueue,
_configBlocks: configBlocks,
_runBlocks: runBlocks,
requires: requires,
name: name,
provider: invokeLaterAndSetModuleName('$provide', 'provider'),
factory: invokeLaterAndSetModuleName('$provide', 'factory'),
service: invokeLaterAndSetModuleName('$provide', 'service'),
value: invokeLater('$provide', 'value'),
constant: invokeLater('$provide', 'constant', 'unshift'),
decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
config: config,
run: function(block) {
runBlocks.push(block);
return this;
}
};
if (configFn) {
config(configFn);
}
return moduleInstance;
function invokeLater(provider, method, insertMethod, queue) {
if (!queue) queue = invokeQueue;
return function() {
queue[insertMethod || 'push']([provider, method, arguments]);
return moduleInstance;
};
}
function invokeLaterAndSetModuleName(provider, method) {
return function(recipeName, factoryFunction) {
if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
invokeQueue.push([provider, method, arguments]);
return moduleInstance;
};
}
});
};
});
}
Why is it better?
Both approaches do the same so developer will decide what is better for his project structure
for efficiency?
There is no efficiency value measurement, both has same efficiency. No performance penalty.
for what?
In project I want to write each directive each controller each ... in separate file so I use app.controller(...);, app.service(...); ,...
However common directives I want to put in one file so I use:
app.directive(…).directive(…).directive(…).directive(…).directive(…)
Hope it will spread the light on your understanding :)

Singleton JS Object in Angular Service

I am trying to add methods to an Object's protoype, which will be used in a singleton service and will be initiated only once when the service is created.
angular
.module('app.steps')
.factory('stepsService', stepsService);
stepsService.$inject = [];
/* #ngInject */
function stepsService() {
var steps = new Steps(1,3);
function Steps(current_step, total_steps) {
this.c_step = current_step;
this.t_step = total_steps;
}
Steps.prototype = {
addSteps: function (num) {
this.c_step += num;
},
setLastStep: function () {
this.lastStep = this.c_step = this.t_step;
}
};
var service = {
steps: steps
};
return service;
}
My problem is that although the object is created and initiated successfully, the methods are not there.
What is missing?
As mentioned in the comments, var steps = new Steps(1,3); should be after Steps.prototype = {....}

2 way binding issues with directives, controllers and services

This is bugging me a bit.
I have a service that handles logo panels and a function that is used to navigate between the different panels.
When getPanels is invoked it sets the currentPanel, index and length on the service when all promises have completed (see $q.all in the getPanels method):
.service('ConfiguratorLogoService', ['$q', 'UploadService', 'LogoService', 'ArrayService', 'SvgService', function ($q, uploadService, logoService, arrayService, helper) {
// Private function to build a file array
var _buildFileArray = function (panels, files) {
//--- Omitted for brevity ---//
};
// Create our service
var service = {
// Create our arrays
panels: [],
files: [],
currentPanel: null,
index: 0,
length: 0,
// Get our panels
getPanels: function (container, garmentId) {
// Create a deferred promise
var deferred = $q.defer();
// Create our arrays
var panels = []
files = [],
promises = [];
// If we have a container
if (container) {
// Get the containers children
var children = container.children()
// Loop through our panel's children
for (var i = 0; i < children.length; i++) {
// Get the current child
var child = angular.element(children[i]),
childId = child.attr('id'),
childTitle = helper.extractText(childId, ':', 1);
// Create our item
var panel = {
id: childId,
title: childTitle
};
// Try to get our item
promises.push(logoService.get(garmentId, panel.id).then(function (response) {
// If we have any data
if (response) {
// Add the file to our array
files.push(response);
}
}));
// Add our child to the array
panels.push(panel);
}
}
// After all the promises have been handled
$q.all(promises).then(function () {
// Get our files
service.files = _buildFileArray(panels, files);
service.panels = panels;
service.currentPanel = panels[0];
service.length = panels.length;
// Resolve our promise
deferred.resolve({
files: service.files,
panels: panels
});
});
// Return our promise
return deferred.promise;
},
// Get our next panel
navigateNext: function () {
// Create a deferred promise
var deferred = $q.defer();
// Get the next index or reset if we reached the end of our list
service.index = service.index === (service.length - 1) ? 0 : service.index += 1;
// Set our active panel
service.currentPanel = service.panels[service.index];
console.log(service.index);
// Resolve our promise
deferred.resolve();
// Return our promise
return deferred.promise;
},
// Get our previous panel
navigatePrevious: function () {
// Get the previous index or set to the end of our list
service.index = service.index === 0 ? service.length - 1 : service.index -= 1;
// Set our active panel
service.currentPanel = service.panels[service.index];
},
// Removes the file from azure
remove: function (index) {
//--- Omitted for brevity ---//
}
};
// Return our service
return service;
}])
which is fine, it works and the first panel is selected.
So, I have a controller, which is attached to a directive. The controller looks like this:
.controller('ConfiguratorLogosDirectiveController', ['ConfiguratorLogoService', 'RowService', function (service, rowService) {
var self = this;
// Set our current panel
self.currentPanel = service.currentPanel;
self.index = service.index;
self.length = service.length;
// Initialization
self.init = function (container, garmentId) {
// Get our panels
return service.getPanels(container, garmentId).then(function (response) {
self.panels = response.panels;
self.files = response.files;
// If we have any panels
if (self.panels.length) {
// Set our current panel
self.currentPanel = service.currentPanel;
self.index = service.index;
self.length = service.length;
}
// Return our response
return response;
})
};
// Map our service functions
self.upload = service.upload;
self.next = service.navigateNext;
self.previous = service.navigatePrevious;
self.remove = service.remove;
}])
As you can see, when I get my panels, I set the currentPanel, index and length on the controller itself which I didn't think I would have to do because when the controller is invoked, it already has a reference to the service values. I figured 2 way binding would come into play and when the service values update, the controller would update too.
Anyway, I update the values after the getPanels method completes successfully. In my directive I have this:
// Invoke on controller load
controller.init(container, scope.garmentId).then(function (response) {
// Map our properties
scope.panels = controller.panels;
scope.files = controller.files;
scope.currentPanel = controller.currentPanel;
scope.index = controller.index;
scope.length = controller.length;
});
which again works fine. In my template I can see the first panel and it looks fine.
So, then came the next step which was my navigate functions. I started with next which I have modified for testing purposes so I can output the controller.index as well as the console.log in the service navigation function.
The directive function looks like this:
scope.next = function () {
controller.next().then(function () {
console.log(controller.index);
});
};
When this method is invoked, I can see in my console that the service increases the index by 1 but the controller still shows 0 which means that 2 way binding is not working.
I am about to update my method in the controller to push the currentPanel and index to the controller, but before I do I thought I would ask here first.
So, does anyone know why my 2 way binding isn't working?
So my current workaround works, but I just don't like it.
In my directive I have done this:
scope.next = function () {
controller.next().then(function () {
console.log(controller.index);
scope.currentPanel = controller.currentPanel;
scope.index = controller.index;
scope.length = controller.length;
});
}
and in the directive controller I have done this:
self.next = function () {
// Try to navigate forward
return service.navigateNext().then(function () {
// Set our current panel
self.currentPanel = service.currentPanel;
self.index = service.index;
self.length = service.length;
console.log(self.index);
});
}
and in my service, it looks the same as before:
// Get our next panel
navigateNext: function () {
// Create a deferred promise
var deferred = $q.defer();
// Get the next index or reset if we reached the end of our list
service.index = service.index === (service.length - 1) ? 0 : service.index += 1;
// Set our active panel
service.currentPanel = service.panels[service.index];
console.log(service.index);
// Resolve our promise
deferred.resolve();
// Return our promise
return deferred.promise;
},
This works, but surely this is not the way it should work.
I have figured it out thanks to this article.
I just had to create an object in my directive and bind the values to that.
Doing that fixed the issues.

Can I access MVC web.config values in my angularjs module config method?

How do I pass my MVC web.config values to my angular module config method?
I want to set the $logProvider.debugEnabled value with parameter from my MVC web.config. This way I won't have to remember to change the setting as I move from Dev to Test to Prod.
I have found a solution for passing parameters that are accessible from my angular controller, but I need them in my config method.
Here is what I have now in my app.js
(function () {
var app = angular.module("myApp", ['configSettings']);
app.config('settings', [function (settings, $logProvider) {
var envr = settings.serverConfig;
if (!envr == "LOCAL") {
$logProvider.debugEnabled(false);
}
}]);
}());
Thanks in advance,
John
I'm using a custom http handler for this:
public class JavascriptResourceHandler : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
// Return false in case your Managed Handler cannot be reused for another request.
// Usually this would be false in case you have some state information preserved per request.
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
var sb = new StringBuilder();
sb.Append("var js = {};");
sb.Append("js.settings = { ");
var settings = ConfigurationManager.AppSettings.AllKeys.Where(k => k.StartsWith("JS_")).ToList();
for (int i = 0; i < settings.Count; i++)
{
var key = settings[i];
var name = key.Replace("JS_", string.Empty);
var value = ConfigurationManager.AppSettings[key];
sb.Append(name);
sb.Append(":");
sb.Append("'");
sb.Append(HttpUtility.JavaScriptStringEncode(value));
sb.Append("'");
if (i != settings.Count - 1)
sb.Append(",");
}
sb.Append("};");
context.Response.Clear();
context.Response.ContentType = "text/javascript";
context.Response.Write(sb.ToString());
}
#endregion
}
then add this handler into <system.webServer><handlers>:
<add name="JavascriptResourceHandler" verb="GET" path="JavascriptResourceHandler.axd" type="MyNamespace.JavascriptResourceHandler, MyAssembly, Version=1.0.*, Culture=neutral" />
reference this from html:
<script src="~/JavascriptResourceHandler.axd"></script>
then use JS_ prefix for config values to propagate these to javascript (this is for security reason because we do not want usually propagate all app settings to client side code):
<add key="JS_Key" value="MyConfigValue" />
config values will be available in javascript:
var myValue = js.settings.Key; // the JS_ prefix will be automatically removed
your example:
(function () {
var app = angular.module("myApp", ['configSettings']);
app.config('settings', [function (settings, $logProvider) {
var envr = js.settings.serverAppSettingsKeyWithoutJSPrefix;
if (!envr == "LOCAL") {
$logProvider.debugEnabled(false);
}
}]);
}());

How to Nested use “Require.js” with "backbone.js"?

I'm doing the application, the use of backbone.js and require.js, I would like to achieve dynamic configuration module navigation by the "backbone.router" function, here is my question?
This is my baserouter defined,I want to achieve dynamic load "backbone.view" according to "the viewPath" parameter.How can I do?
define(['require', 'underscore', 'backbone'], function(require, _, Backbone) {
var BaseRouter = Backbone.Router.extend({
container: "#page",
loadView: function(viewPath) {
**//Here require lazy loading "base/people/view.js", **
**//I do not know how to achieve it?**
var view = require(viewPath);//viewPath = "base/people/view";
this._currentView = new view();
this._currentView.render();
$(this.container).html(this._currentView.el);
}
});
return BaseRouter;
});
This is the definition of the router, it work with "baserouter" to dynamically set the navigation menu.
define(['baserouter'], function(baserouter) {
//The JSON data should come from the database,
//These data define the navigation information for all modules.
var navs = JSON.parse('[{"name": "people","title": "peoplemanage","view": "base/people/view"},{"name": "test","title": "testmanage","view": "pub/test/view"}]');
var AppRouter = baserouter.extend();
for (var i = 0, l = navs.length; i < l; i++) {
var nav = navs[i];
AppRouter.prototype["loadView_" + nav.name] = function() {
var path = nav.view;
return function() {
AppRouter.prototype.loadView(path);
}
}();
}
var initialize = function() {
var routes = {}
for (var i = 0, l = navs.length; i < l; i++) {
var nav = navs[i];
routes[nav.name] = "loadView_" + nav.name;
}
var app_router = new AppRouter({
"routes": routes
});
Backbone.history.start();
};
return {
initialize: initialize
};
});
Here is the html code for the navigation menu:
<ul class="dropdown-menu">
<li>people</li>
<li>test</li>
</ul>
This method can achieve.But I'm not sure this is the best practice, and who has a better way?
loadView: function(viewPath) {
var _this = this;
if (this._currentView) {
this._currentView.dispose();
}
//var view = require(viewPath);
//**This method can achieve.But I'm not sure this is the best practice, and who has a better way?**
//setTimeout(function() {
require([viewPath], function(view) {
_this._currentView = new view();
_this._currentView.render();
$("#page").html(_this._currentView.el);
});
//}, 100);

Resources