Dojo CompoButton - onClick event of MenuItem issue with its function - dojo

I have the following code in dojo creating a ComboButton with several MenuItems of the correlated Menu:
testfunc = function (input) {
//Code that uses input
}
var menu = new Menu({
id: "saveMenu"
});
//array has 10 json objects and each object has an "Id" field
for(var i=0; i<10; i++) {
var temp = array[i]["Id"];
var menuItem1 = new MenuItem({
label: "Option"+i,
onClick: function() { testfunc(temp); }
});
menu.addChild(menuItem1);
}
var comboButton = new ComboButton({
optionsTitle: "Options",
label: "Combo",
dropDown: menu,
---> onClick:function(){ console.log("Clicked ComboButton"); }
}, "combo");
comboButton.startup();
menu.startup();
I need for each MenuItem its onClick function to pass a different variable in the testfunc. Specifically the value of the array[i]["Id"] which is an array that has 10 json objects. With the above code the parameter that is passed in ALL the testfunc functions of ALL the MenuItems is the last one.
Is it possible to pass for each MenuItem the correct array[i]["Id"] value
i.e.
MenuItem0 -> onClick: testfunc(array[0]["Id"])
MenuItem1 -> onClick: testfunc(array[1]["Id"])
MenuItem2 -> onClick: testfunc(array[2]["Id"])
.
.
.
etc
Thanks

I have found a solution to my problem and I think I should share it.
The issue was solved by passing in the testfunc of the onClick event of the menuItem the parameters using the "this" token e.g.
var menuItem1 = new MenuItem({
id: array[i]["Id"],
label: "Option"+i,
onClick: function() { testfunc(this.id, this.label); }
});

Related

Odoo: open full model page from add line in a one2many field

I made a custom model and i have a one2many field where i click on "add new line" then "search more" to look for the item i want to add just like the picture below
What I want is to click on "add new line", and the whole products page appear without having to go throw the previous process
You can extend ListRenderer and override _onAddRecord to open a search more view using a condition in context.
The following code adds a new widget to open search more... link and select many records.
odoo.define('stack_overflow.open_list_view', function (require) {
"use strict";
var pyUtils = require('web.py_utils');
var dialogs = require('web.view_dialogs');
var core = require('web.core');
var _t = core._t;
var fieldRegistry = require('web.field_registry');
var ListRenderer = require('web.ListRenderer');
var AddManyFieldOne2ManyRenderer = ListRenderer.extend({
/**
* It will returns the first visible widget that is editable
*
* #private
* #returns {Class} Widget returns first widget
*/
_getFirstWidget: function () {
var record = this.state.data[this.currentRow];
var recordWidgets = this.allFieldWidgets[record.id];
var firstWidget = _.chain(recordWidgets).filter(function (widget) {
var isLast =
widget.$el.is(':visible') &&
(
widget.$('input').length > 0 || widget.tagName === 'input' ||
widget.$('textarea').length > 0 || widget.tagName === 'textarea'
) &&
!widget.$el.hasClass('o_readonly_modifier');
return isLast;
}).first().value();
return firstWidget;
},
add_rows: function (lines, field_name) {
var self = this;
if (lines.length > 0) {
self.trigger_up('add_record', {
context: [{ [field_name]: lines[0] }],
forceEditable: "bottom",
allowWarning: true,
onSuccess: function () {
self.unselectRow();
lines.shift(); // Remove the first element after adding a line
self.add_rows(lines, field_name);
}
});
}
},
_onAddRecord: function (ev) {
// we don't want the browser to navigate to a the # url
ev.preventDefault();
// we don't want the click to cause other effects, such as unselecting
// the row that we are creating, because it counts as a click on a tr
ev.stopPropagation();
var self = this;
// but we do want to unselect current row
this.unselectRow().then(function () {
var context = ev.currentTarget.dataset.context;
if (context && pyUtils.py_eval(context).open_list_view) {
// trigger add_record to get field name and model
// you do not need to trigger add_record if you pass the field name and model in context
self.trigger_up('add_record', {
context: [{}],
onSuccess: function () {
var widget = self._getFirstWidget();
var field_name = 'default_' + widget.name;
var model = widget.field.relation;
self.unselectRow();
self._rpc({
model: model,
method: 'search',
args: [[]],
}).then(
function (result) {
return new dialogs.SelectCreateDialog(self, _.extend({}, self.nodeOptions, {
res_model: model,
context: context,
title: _t("Search: add lines"),
initial_ids: result,
initial_view: 'search',
disable_multiple_selection: false,
no_create: !self.can_create,
on_selected: function (records) {
self.add_rows(records.map(product => product.id), field_name);
}
})).open();
});
}
});
} else {
self.trigger_up('add_record', { context: context && [context] });
}
});
},
});
var AddManyFieldOne2Many = fieldRegistry.map.section_and_note_one2many.extend({
/**
* We want to use our custom renderer for the list.
*
* #override
*/
_getRenderer: function () {
if (this.view.arch.tag === 'tree') {
return AddManyFieldOne2ManyRenderer;
}
return this._super.apply(this, arguments);
},
});
fieldRegistry.add('add_many_one2many', AddManyFieldOne2Many);
});
To use it define the widget attribute of the one2Many field to
<field name="field_name" widget="add_many_one2many"...
And add the following option to the tree view of the one2Many field
<create string="Open list view" context="{'open_list_view': True}"/>
Salam Ahmed
you can create a function that take you directly to product page and assigned to a button like in wizard's when you click to create a new view or get data from another view or page

Dojo ListItem with Child Inputs

I have a dojo list item that is clickable.. But at the same time we like to put input elements inside the list item. The problem is that if you click on the child element(example checkbox) the listitem onclick intercepts the call first(which seems opposite of the html bubble up format). So we cannot call stoppropagation on the child element to stop the listitem from changing the page.
In the example below you will see the listitem alert come up before the checkbox alert..
How do you handle having input elements in a listitem without triggering the listitem..
fiddle::http://jsfiddle.net/theinnkeeper/HFA36/1/
ex.
var list1 = registry.byId("myList");
var item = new ListItem ({
label: "A \"programmatic\" ListItem",
moveTo: "#",
noArrow:true,
onClick : function() {
alert("listItem clicked !" + event.target.type);
}
});
list1.addChild(item);
var check = new cb({onClick:function(){alert("checkbox clicked");event.stopPropagation();}});
check.placeAt(item.containerNode.firstChild);
check.startup();
I had a similar problem a while back and noticed that the dojox/mobile/ListItem is not really great when adding extra event handlers to it (checkboxes, touch gestures, ...), so to solve that I usually extend dojox/mobile/ListItem and fix the events by myself.
For example:
var CheckedListItem = declare("dojox/mobile/CheckedListItem", [ ListItem ], {
_initializeCheckbox: function() {
this.checkbox = new CheckBox({
});
domConstruct.place(this.checkbox.domNode, this.containerNode.firstChild, "last");
this.checkbox.startup();
this.checkbox.onClick = this.onCheckboxClick;
},
onCheckboxClick: function() { },
_setOnCheckboxClickAttr: function(handler) {
this.onCheckboxClick = handler;
if (this.checkbox !== null && this.checkbox !== undefined) {
this.checkbox.onClick = handler;
}
},
_onClick: function(e) {
if (e.target !== this.checkbox.domNode) {
this.inherited(arguments);
}
},
postCreate: function() {
this.inherited(arguments);
this._initializeCheckbox();
}
});
Due to overriding _onClick() and adding additional checks I managed to get the intended behavior.
A full example can be found here: http://jsfiddle.net/LQ6Mb/

Add a custom button in column header dropdown menus {EXTJS 4}

I want a button in column header dropdown menu of grid in extjs4.
so that i can add or delete columns which are linked in database.
Any help will be appreciated...
Thankyou..:)
Couple of months ago I had the same problem. I've managed to solve it by extending Ext.grid.header.Container (I've overrided getMenuItems method). However, recently, I've found another solution which requires less coding: just add menu item manualy after grid widget is created.
I'll post the second solution here:
Ext.create('Ext.grid.Panel', {
// ...
listeners: {
afterrender: function() {
var menu = this.headerCt.getMenu();
menu.add([{
text: 'Custom Item',
handler: function() {
var columnDataIndex = menu.activeHeader.dataIndex;
alert('custom item for column "'+columnDataIndex+'" was pressed');
}
}]);
}
}
});
Here is demo.​
UPDATE
Here is demo for ExtJs4.1.
From what I have been seeing, you should avoid the afterrender event.
Context:
The application I am building uses a store with a dynamic model. I want my grid to have a customizable model that is fetched from the server (So I can have customizable columns for my customizable grid).
Since the header wasn't available to be modified (since the store gets reloaded and destroys the existing menu that I modified - using the example above). An alternate solution that has the same effect can be executed as such:
Ext.create('Ext.grid.Panel', {
// ...
initComponent: function () {
// renders the header and header menu
this.callParent(arguments);
// now you have access to the header - set an event on the header itself
this.headerCt.on('menucreate', function (cmp, menu, eOpts) {
this.createHeaderMenu(menu);
}, this);
},
createHeaderMenu: function (menu) {
menu.removeAll();
menu.add([
// { custom item here }
// { custom item here }
// { custom item here }
// { custom item here }
]);
}
});
For people who would like to have not just one "standard" column menu but have an individual columnwise like me, may use the following
initComponent: function ()
{
// renders the header and header menu
this.callParent(arguments);
// now you have access to the header - set an event on the header itself
this.headerCt.on('menucreate', function (cmp, menu, eOpts) {
menu.on('beforeshow', this.showHeaderMenu);
}, this);
},
showHeaderMenu: function (menu, eOpts)
{
//define array to store added compoents in
if(this.myAddedComponents === undefined)
{
this.myAddedComponents = new Array();
}
var columnDataIndex = menu.activeHeader.dataIndex,
customMenuComponents = this.myAddedComponents.length;
//remove components if any added
if(customMenuComponents > 0)
{
for(var i = 0; i < customMenuComponents; i++)
{
menu.remove(this.myAddedComponents[i][0].getItemId());
}
this.myAddedComponents.splice(0, customMenuComponents);
}
//add components by column index
switch(columnDataIndex)
{
case 'xyz': this.myAddedComponents.push(menu.add([{
text: 'Custom Item'}]));
break;
}
}
I took #nobbler's answer an created a plugin for this:
Ext.define('Ext.grid.CustomGridColumnMenu', {
extend: 'Ext.AbstractPlugin',
init: function (component) {
var me = this;
me.customMenuItemsCache = [];
component.headerCt.on('menucreate', function (cmp, menu) {
menu.on('beforeshow', me.showHeaderMenu, me);
}, me);
},
showHeaderMenu: function (menu) {
var me = this;
me.removeCustomMenuItems(menu);
me.addCustomMenuitems(menu);
},
removeCustomMenuItems: function (menu) {
var me = this,
menuItem;
while (menuItem = me.customMenuItemsCache.pop()) {
menu.remove(menuItem.getItemId(), false);
}
},
addCustomMenuitems: function (menu) {
var me = this,
renderedItems;
var menuItems = menu.activeHeader.customMenu || [];
if (menuItems.length > 0) {
if (menu.activeHeader.renderedCustomMenuItems === undefined) {
renderedItems = menu.add(menuItems);
menu.activeHeader.renderedCustomMenuItems = renderedItems;
} else {
renderedItems = menu.activeHeader.renderedCustomMenuItems;
menu.add(renderedItems);
}
Ext.each(renderedItems, function (renderedMenuItem) {
me.customMenuItemsCache.push(renderedMenuItem);
});
}
}
});
This is the way you use it (customMenu in the column config let you define your menu):
Ext.define('MyGrid', {
extend: 'Ext.grid.Panel',
plugins: ['Ext.grid.CustomGridColumnMenu'],
columns: [
{
dataIndex: 'name',
customMenu: [
{
text: 'My menu item',
menu: [
{
text: 'My submenu item'
}
]
}
]
}
]
});
The way this plugin works also solves an issue, that the other implementations ran into. Since the custom menu items are created only once for each column (caching of the already rendered version) it will not forget if it was checked before or not.

Ext JS 4 - how to change content of an item which can't be referenced by an ID

I would really appreciate any help with the following problem:
I need to be able to change content of an item (div or textfield) but the problem is that there are going to be multiple instances of the same window so I cannot use div IDs.
I tried this little example:
var myBtnHandler = function(btn) {
myPanel.items.items[0].html = "Changed by click!";
myPanel.doLayout();
}
var fileBtn = new Ext.Button({
text : 'Change',
handler : myBtnHandler
});
var panel1 = {
html : 'Original content.'
};
var myPanel = new Ext.Window({
title : 'How to change it?',
items : [
panel1,
fileBtn
]
});
myPanel.items.items[0].html = "Changed on load!";
myPanel.show();
Referencing an element by myPanel.items.items[0] works on load but does not work when it's in the button handler - is it a scope-related problem? How to reference an element without its ID?
Thank you very much,
H.
The problem has nothing to do with scope. The first time you set the html property, the component has not yet been rendered, so on initial render it will read the html property off the component. The second time, you're just setting a property on an object, it's not going to react in any way.
Instead, you should use the update() method.
Ext.require('*');
Ext.onReady(function() {
var myBtnHandler = function(btn) {
myPanel.items.first().update("Changed by click!");
}
var fileBtn = new Ext.Button({
text: 'Change',
handler: myBtnHandler
});
var panel1 = {
html: 'Original content.'
};
var myPanel = new Ext.Window({
title: 'How to change it?',
items: [panel1, fileBtn]
});
myPanel.items.items[0].html = "Changed on load!";
myPanel.show();
});
There are several functions that go through elements that belongs to a container. Try using for example down():
btn = panel.down('button');
Where 'button' parameter would mean 'give me element which type is equal to 'button'. Check out Sencha doc for querying various elements too: http://docs.sencha.com/ext-js/4-0/#!/api/Ext.ComponentQuery
Following on from Sha's reply. To put his advice in context with your example.
var myBtnHandler = function(btn) {
btn.up('window').down('panel').update('Changed by click!');
}
var fileBtn = new Ext.Button({
text : 'Change',
handler : myBtnHandler
});
var panel1 = {
html : 'Original content.'
};
var myPanel = new Ext.Window({
title : 'How to change it?',
items : [
panel1,
fileBtn
]
});
myPanel.show();
myPanel.update('Changed on load!');

Dojo populate combo box widget dynamically

Could someone please explain to me why this simple straight forward code isnt working,
var serviceStore = new dojo.data.ItemFileWriteStore({
data: {identifier: "serviceCode",items:[]}
});
//jsonObj is a json object that I obtain from the server via AJAX
for(var i = 0; i<jsonObj.length;i++){
serviceStore.newItem({serviceCode: jsonObj[i]});
}
var serviceFilterSelect = dojo.byId('serviceSelect');
serviceFilterSelect.store = serviceStore;
There is no error at all displayed but my combobox with the id "serviceSelect" doesn't display any options, the combo is declared in the html section of my code,
<input dojoType = "dijit.form.ComboBox" id="serviceSelect"></input>
Any pointers towards the right direction will be much appreciated.
First of all you should use dijit.byId to get dojo widget instead of dojo.byId.
Also every item in jsonObj should contains field "name". This field will be displayed in combobox. E.g:
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dijit.form.ComboBox");
var storeData = {
identifier: 'serviceCode',
items: []
}
var jsonObj = [{
serviceCode: 'sc1',
name: 'serviceCode1'
},
{
serviceCode: 'sc2',
name: 'serviceCode2'
}]
dojo.addOnLoad(function () {
var serviceStore = new dojo.data.ItemFileWriteStore({ data: storeData });
for (var i = 0; i < jsonObj.length; i++) {
serviceStore.newItem(jsonObj[i]);
}
var serviceFilterSelect = dijit.byId('serviceSelect');
serviceFilterSelect.attr('store', serviceStore);
});
And HTML:
<select dojotype="dijit.form.ComboBox" id="serviceSelect" ></select>
It seems that it works.
I can't tell from the code you posted, but if you're having trouble getting the DOM nodes, they may not had a chance to get loaded.
You can try wrapping what you have above with a dojo.ready(function(){ ... });.
Have you put items in your store? I can't tell from the sample that you posted.
var serviceStore = new dojo.data.ItemFileWriteStore({
data: {
identifier: "serviceCode"
,items: [
{serviceCode:'ec', name:'Ecuador'}
,{serviceCode:'eg', name:'Egypt'}
,{serviceCode:'sv', name:'El Salvador'}
]
}
});
For dojo >= 1.6:
dojo.byId('serviceSelect').store=serviceStore;
For dojo < 1.6:
dojo.byId('serviceSelect').attr("store",serviceStore);