So I have this checkbox group
b-form-checkbox-group.ml-4.b-check(
v-model="selected",
v-if="productGroup.show",
:options="productGroup.products",
I am trying to have the list of :options to be checked by default. I am having issues with settign this up.
Here is my code, I don't know why it is not working.
toggleAll(checked, productGroup) {
if (checked) {
let productIds = this.selected ? [...this.selected] : [];
productGroup.products.forEach((product) => {
productIds.push(product.id);
});
this.selected = _.uniq(productIds);
} else {
let temp = [...this.selected];
productGroup.products.forEach((product) => {
if (temp.indexOf(product.id) > -1) {
temp.splice(temp.indexOf(product.id), 1);
}
});
this.selected = temp;
}
Assume your option group is an array of object data, and you will use another array to track the selected options, as follows:
data() {
return {
selected1: [],
group1: [
{ id: 1, text: 'Item 1', value: 'item1' },
{ id: 2, text: 'Item 2', value: 'item2' }
]
}
}
Your toggleAll method needs to add all the group's values to the tracking array to toggle them on, and it needs to remove all values to toggle them off:
methods: {
toggleAll(checked, group, selected) {
// First remove any selected items, regardless of the toggle state
selected.splice(0, selected.length)
// Now, if the toggle is true, add all items
if (checked) {
group.forEach(o => selected.push(o.value));
}
}
}
Using the example data above, call the above method like:
this.toggleAll(true, this.group1, this.selected1);
Related
I need to bind an object from checkboxes, and in this example, a checkbox is its own component:
<input type="checkbox" :value="option.id" v-model="computedChecked">
Here's my data and computed:
data() {
return {
id: 1,
title: 'test title',
checked: {
'users': {
},
},
}
},
computed: {
computedChecked: {
get () {
return this.checked['users'][what here ??];
},
set (value) {
this.checked['users'][value] = {
'id': this.id,
'title': this.title,
}
}
},
....
The above example is a little rough, but it should show you the idea of what I am trying to achieve:
Check checkbox, assign an object to its binding.
Uncheck and binding is gone.
I can't seem to get the binding to worth though.
I assume you want computedChecked to act like an Array, because if it is a Boolean set, it will receive true / false on check / uncheck of the checkbox, and it should be easy to handle the change.
When v-model of a checkbox input is an array, Vue.js expects the array values to stay in sync with the checked status, and on check / uncheck it will assign a fresh array copy of the current checked values, iff:
The current model array contains the target value, and it's unchecked in the event
The current model array does not contain the target value, and it's checked in the event
So in order for your example to work, you need to set up your setter so that every time the check status changes, we can get the latest state from the getter.
Here's a reference implementation:
export default {
name: 'CheckBoxExample',
data () {
return {
id: 1,
title: 'test title',
checked: {
users: {}
}
}
},
computed: {
computedChecked: {
get () {
return Object.getOwnPropertyNames(this.checked.users).filter(p => !/^__/.test(p))
},
set (value) {
let current = Object.getOwnPropertyNames(this.checked.users).filter(p => !/^__/.test(p))
// calculate the difference
let toAdd = []
let toRemove = []
for (let name of value) {
if (current.indexOf(name) < 0) {
toAdd.push(name)
}
}
for (let name of current) {
if (value.indexOf(name) < 0) {
toRemove.push(name)
}
}
for (let name of toRemove) {
var obj = Object.assign({}, this.checked.users)
delete obj[name]
// we need to update users otherwise the getter won't react on the change
this.checked.users = obj
}
for (let name of toAdd) {
// update the users so that getter will react on the change
this.checked.users = Object.assign({}, this.checked.users, {
[name]: {
'id': this.id,
'title': this.title
}
})
}
console.log('current', current, 'value', value, 'add', toAdd, 'remove', toRemove, 'model', this.checked.users)
}
}
}
}
I have a very complicated state in my application like:
state: {
formFieldList: [
{
name: 'RadioGroup',
schema: {
edit:true,
label:"Radios",
name:"Radios",
type:"radios",
values: [
{
labelName: 'default',
value: 'default',
}
{
labelName: 'default2',
value: 'default',
}
]
},
{
name: 'RadioGroup1',
schema: {
edit:true,
label:"Radios1",
name:"Radios1",
type:"radios1",
values: [
{
labelName: 'default',
value: 'default',
}
{
labelName: 'default2',
value: 'default',
}
]
},
},
],
}
As Vuejs Array Changing Caveats, I use Vue.set in my mutation.When I set values array in one fieldList item,It will update all RadioGroup values.
updateOptionValue(state, { index, optionIndex, value }) {
let values = state.formFieldList[index].schema.values;
let labelName = values[optionIndex].labelName;
Vue.set(values, optionIndex, {
labelName,
value,
});
}
How can I handle nested array update?
Here's my simple demo on codesandbox https://codesandbox.io/s/w6nq81l808
As I add two radiogrouops to the red area(click radiogroup twice), and edit the two radiosgroups simultaneously, the value will change in the same time.
You can assign state attribute to new object to make it reactive:
updateOptionValue(state, { index, optionIndex, value }) {
let values = state.formFieldList[index].schema.values;
let labelName = values[optionIndex].labelName;
values[optionIndex] = {
value,
labelName
}
state.formFieldList = JSON.parse(JSON.stringify(state.formFieldList)) // assign new object
}
I have store having structure :
Ext.create('Ext.data.Store', {
fields: [
'title'
],
data: [{
title: 'ABC'
}, {
title: 'ABC2'
}, {
title: 'ABC3'
}, {
title: 'ABC4'
}, {
title: 'ABC5'
}, {
title: 'ABC6'
}]
});
So when I load this store List get populated with all 6 records.
I just wanted to Filter this store on button click I just wanted to get some selected record out of this 6 record Can It be possible.
Provide me Some Idea or Working code.
To filter the store based on title
Ext.getStore('storeId').filter("title", "ABC3");
To clear filter
Ext.getStore('storeId').clearFilter();
See store filter doc
Update
Ext.getStore('storeId').filterBy(function(record){
var title = record.get('title');
if(title == "ABC" || title == "ABC1" || title == "ABC2")
return record;
});
My approach is to set a filter on the store when I tap on the button. In my case it was a selectfield and on the change event I filter compared to the current value in the selectfield
onChangeStatusSelectfield: function (newValue, oldValue) {
var store = Ext.getStore('CustomVacationRequest');
console.log('Accepted Filter');
newValue = this.getStatusSelectfield().getValue();
console.log(store, newValue);
store.clearFilter();
if (store != null);
store.filter(function (record) {
if (newValue == record.data.status) { //your data from the store compared to
//the value from the selectfield
return true;
}
Ext.getCmp("VacationRequestsManagerList").refresh() //refresh your list
});
},
This is just my part of the controller. Handle events and buttons and stores at your own choice&need. Good luck!
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.
I have an application in MVC with a view class:
Ext.define('a.view.Mainmenu' ,{
extend: 'Ext.menu.Menu',
alias: 'widget.mainmenu',
text: 'Menu',
items: [
{
xtype: 'menucheckitem',
id: 'mci1',
text: 'a'
},
{
xtype: 'menucheckitem',
id: 'mci2',
text: 'b'
}]
});
How I can control the click events of the menucheckitems in the controller? I want to check if the menucheckitems are checked.
I tried something in the init function of the controller, but there is an error (item.down("mci1") is null):
...
init: function() {
this.control({
'mainmenu': {
click: function(item) {
if (item.down('mci1').checked == true) {
...
}
if (item.down('mci2').checked == true) {
...
}
}
}
});
}
How I could do it right?
#Ringo,
Neither the menuitem or menucheckitem have a method of down() available to them according to the Sencha docs (http://docs.sencha.com/ext-js/4-0/#!/api/Ext.menu.CheckItem-event-checkchange).
So, that is why those aren't working.
There is an event for the xtype of menucheckitem called 'checkchange'. This event makes the following arguments available for your function:
this (Ext.menu.CheckItem) <= the actual menucheckitem that was checked/unchecked (so mci1 or mci2 depending on which the user clicked on)
checked (Boolean) <= true if the change set the menucheckitem as checked and false if unchecked.
So, this would only require you to do something like:
...
init: function() {
this.control({
'mainmenu menucheckitem': {
checkchange: function(item, checked) {
if (checked) {
if(item.id == 'mci1'){
...
}
}else{
...
}
}
}
});
}
The item parameter is already your menu item. You don't have to query down.
so it would be:
if(item.checked && item.getId() == 'mci1'){
...
}