QML How to make QAbstractTableModel with qml tableview checkable - checkbox

I’m new in QT. I want to make every row of qml tableview checkable but it doesn’t work.
The tableview with data is shown successfully but it’s not checkable.
It seems that flags() and setData() functions are never run and role==Qt::CheckStateRole never be true.
Please help.
C++ code
I modified code like but it doesn't work:
QVariant TableModel::data(const QModelIndex & index, int role) const {
if (index.row() < 0 || index.row() >= _fields->size())
return QVariant();
if(role == Qt::CheckStateRole) {
return rowsChk.contains(index.row()) ? Qt::Checked : Qt::Unchecked;
}
switch(role) {
case NameRole:
return model.name();
case DescriptionRole:
return model.description();
case TypeRole:
return model.type();
}
return QVariant();
}
bool TableModel::setData(const QModelIndex & index, const QVariant & value, int role){
rowsChecked(index.row(), value) ;
emit dataChanged(index, index);
return true;
}
here is my qml file
TableView {
model: tableModel
anchors.fill: parent
frameVisible: true
headerVisible: true
sortIndicatorVisible: false
alternatingRowColors: true
Component {
id: checkBoxDelegate
Item {
CheckBox {
anchors.fill: parent
checked: styleData.value
}
}
}
TableViewColumn {
role: "check"
title: ""
width: 30
delegate: checkBoxDelegate
}
TableViewColumn {
role: "name"
title: "Name"
width: 200
}
TableViewColumn {
role: "description"
title: "Description"
width: 100
}
TableViewColumn {
role: "type"
title: "Type"
width: 100
}

The setData() method is not called because you probably haven't reimplemented method:
QHash<int, QByteArray> roleNames() const;
which is a virtual method of QAbstractItemModel, the class that QAbstractTableModel inherits. Without this the QML does not know what your roles mean and does not care about your data. In your case this method should be defined this way:
QHash<int, QByteArray> FieldModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[DescriptionRole] = "description";
roles[FilterRole] = "filter";
roles[TypeRole] = "type";
return roles;
}
Personally, I have no idea why the
Qt::ItemFlags flags(const QModelIndex &index) const;
method is not called - have the same problem. But you can solve it by defining a delegate of TableViewColumn in your QML:
Component {
id: checkBoxDelegate
Item {
CheckBox {
anchors.fill: parent
checked: styleData.value
}
}
}
and then assign this delegate to your columns:
TableViewColumn {
role: "name"
title: "Name"
width: 200
delegate: checkBoxDelegate
}
Then the column contains a CheckBox control which value is specified by method
QVariant FieldModel::data(const QModelIndex & index, int role) const;
just return value 0 or QString("false") for unchecked and 1 or QString("true") for checked state.

Related

In a Salesforce LWC app how can I add checkbox values to an object's field and show it in a data-table?

I have made my research, but couldn't find the answer.
I have a Lightning App in Salesforce, I used LWC Js and Apex.
In one part of the app the user can add a 'desk item' (by typing its name) and select from a checkbox 1-2 items to add them to the 'desk'.
I used Apex to transfer the value of the 'desk item' to an Object and I can show it in a list (in the app).
How can I add the checkbox value(s) to the submitDesk(){...} so it sends its value(s) along with the 'desk item' value?
I don' know where/how exactly to add and to get it back?
The JS Code
import { LightningElement, track } from 'lwc';
import createDesk from '#salesforce/apex/FlexOfficeController.createDesk';
import getDesks from '#salesforce/apex/FlexOfficeController.getDesks';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class DeskList extends LightningElement {
// Desk
#track data_desks = [];
//table to show the desks's Id + Name, and the checkbox
columns = [
{ label: 'Id', fieldName: 'Id', type: 'text' },
{ label: 'Name', fieldName: 'Name', type: 'text' },
{ label: 'Accessories', fieldName: **the checkbox value**, type: 'text' }
];
// value to the picklist
connectedCallback(){
this.retreiveDesk();
}
retreiveDesk(){
getDesks({})
.then(d => {
this.data_desks = JSON.parse(d);
})
}
desk = {};
changeValue(event){
this.desk[event.target.name] = event.target.value
}
submitDesk(){
console.log(this.desk, this.value + 'Hi there');
createDesk({desk:JSON.stringify(this.desk)})
.then(data=> {
console.log(data + 'hello');
this.retreiveDesk();
// toaster
const evt = new ShowToastEvent({
title: "New desk",
message: `succefully created. Check out your reservation.`,
variant: "success"
})
this.dispatchEvent(evt);
})
}
// Checkbox
value = [];
get options() {
return [
{ label: 'Mouse', value: 'mouse' },
{ label: 'Screen', value: 'screen' },
];
}
// put the checkbox values into a string ('join')
get checkboxValues() {
console.log(this.value);
return this.value.join(',');
}
handleCheckboxChange(event) {
this.value = event.detail.value;
}
}
Apex Controller
public class FlexOfficeController {
#AuraEnabled
public static string createDesk(String desk){
try {
Desk__c d = (Desk__c)JSON.deserialize(desk, Desk__c.class);
insert d;
return d.id;
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
#AuraEnabled
public static string getDesks(){
try {
List<Desk__c> desks = new List<Desk__c> ();
desks = [SELECT Id, Name FROM Desk__c];
return JSON.serialize(desks);
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
}
HTML
<template>
<lightning-card>
<div class="slds-m-around_medium slds-theme_alert-texture">
<lightning-input name="Name" label="Name your desk" onchange={changeValue}></lightning-input>
<lightning-checkbox-group name="Accessories" label="Checkbox Group" options={options} value={value}
onchange={handleCheckboxChange}></lightning-checkbox-group>
<p>{checkboxValues}</p>
<lightning-button onclick={submitDesk} label="Submit"></lightning-button>
<lightning-datatable key-field="id" data={data_desks} columns={columns} hide-checkbox-column></lightning-datatable>
</div>
</lightning-card>
</template>

Handle multiple radio buttons in a tree view with React

I have a dataset with menu items and nested sub-menu items, coming from a GET request. With that, I render a table in a react-hook-form for assigning access permissions on each menu item.
[
{
moduleId: 2,
moduleName: "Menu 1",
permission: 0,
subModuleList: [
{
moduleId: 7,
moduleName: "Menu 1.1",
permission: 0,
subModuleList: null
},
{
moduleId: 8,
moduleName: "Menu 1.2",
permission: 0,
subModuleList: [
{
moduleId: 11,
moduleName: "Menu 1.2.1",
permission: 0,
subModuleList: null
},
{
moduleId: 33,
moduleName: "Menu 1.2.2",
permission: 0,
subModuleList: null
},
{
moduleId: 49,
moduleName: "Menu 1.2.3",
permission: 0,
subModuleList: null
},
{
moduleId: 68,
moduleName: "Menu 1.2.4",
permission: 0,
subModuleList: null
}
]
}
]
}
]
Each table row is a menu item and has 2 radio buttons (no access & read) if it has children, and 3 radio buttons (no access, read & read/write) if is without children.
What I am trying to accomplish, is that each time a radio button is clicked on a child menu item, I want all the parent menu items to update as well with a relevant checked radio button. I feel really stacked on how to achieve this with react.
Here is a code sandbox link with all the current implementations.
Example use case: When checking the menu item Menu 1.2.2 with read or read/write permissions, the Menu 1.2 and Menu 1 should also have checked the read permission
Thank you in advance for your time!
So after some thought, I came up with a solution. The first step is to identify all the parents of each child is clicked.
function getPath(object, search) {
if (object.moduleId === search) return [object.moduleName];
else if (object.subModuleList || Array.isArray(object)) {
let children = Array.isArray(object) ? object : object.subModuleList;
for (let child of children) {
let result = getPath(child, search);
if (result) {
if (object.moduleId) result.unshift(object.moduleName);
return result;
}
}
}
}
This function returns an array of names of all the parent elements. Then I had to find these parent elements on my dataset and update their values based on my needs. There is also a check if the parent element has children, and one or more are checked with value > 0, to retain the parent as checked.
//get all parent objects
const findId = (object, key, searchTerm, newValue) => {
if (object === null) return;
if (Array.isArray(object)) {
for (const obj of object) {
findId(obj, key, searchTerm, newValue);
}
} else {
if (object.hasOwnProperty(key) && object[key] === searchTerm) {
if (object.subModuleList !== null) {
if (newValue >= "1") newValue = "1";
if (newValue === "0") {
const childActive = object.subModuleList.some(
(element) => element.permission !== "0"
);
if (childActive) {
newValue = "1";
} else {
newValue = "0";
}
}
}
object.permission = newValue;
return object;
}
for (const k of Object.keys(object)) {
if (typeof object[k] === "object") {
findId(object[k], key, searchTerm, newValue);
}
}
}
};
Here you can find an updated code sandbox
I hope that someone will benefit from this solution. Also, I want to mention that the code presented here, is also from answers of other devs on other topics.

ExtJS 6 - Bind disabled property to new records in a store

I'm trying to enable/disable a button when the store getNewRecords() function return the length, but not work!
bind: {
disabled: "{!grid.getStore().getNewRecords().length}"
}
Fiddle: https://fiddle.sencha.com/fiddle/1sj5
Someone have idea to how resolve this?
You need to create a formula in your viewmodel:
viewModel: {
formulas: {
hasNewRecords: function (r) {
return this.getView().down("treepanel").getStore().getNewRecords().length > 0;
}
}
}
then you can use it for your bindings:
bind: {
disabled: "{hasNewRecords}"
}
(probably not the best way to get the data you want).
You can read about it here, here and here .
What you're wanting to do here is currently not possible in the framework. Instead, you should create a ViewModel data value and modify that where need be, like this:
var form = Ext.create("Ext.form.Panel", {
viewModel: {
data: {
newRecords: false
}
},
items: [{
xtype: "textfield",
labelField: "Add Child",
name: "col1",
value: "Teste 123"
}],
tbar: {
xtype: "button",
text: "Add new record",
handler: function () {
var data = this.up("form").getForm().getFieldValues();
var rec = grid.getStore().getAt(0);
data["treeCol"] = rec.childNodes.length + 1;
// setting value, so binding updates
this.lookupViewModel().set('newRecords', true);
rec.appendChild(data);
}
},
bbar: {
xtype: "button",
text: "button to disabled when new records",
bind: {
disabled: "{newRecords}"
}
},
renderTo: Ext.getBody()
});
Or by simply doing this.
In your controller:
me.getView().getViewModel().set('storeLen', store.getNewRecords().length);
In your ViewModel, simply do this:
formulas : {
hasNewRecords : {
get : function(get){
var length = get('storeLen') // --> gets the one you set at your controller
return length > 0 ? true : false;
}
}
}
In your View:
bind : {
disabled : '{hasNewRecords}'
}

Change dynamically class in ionic with angular-formly

i have the next code, i want change the class when the value is equal to "abc" but i can't add some class :
$scope.formFields = [
{
key: 'email',
type: 'input',
templateOptions: {
type:'email',
placeholder: 'Correo',
required:true,
class: 'some-clase'
}
}]
now, I know well then the change is done but I can not even add a simple class :
expressionProperties: {
'templateOptions.class': '"glyphicon form-control-feedback glyphicon-"'
}
help me please , if they can add an example in jsbin, codepen... thanks
Your expressionProperties property is a formly expression and can therefore be a function. It should probably be something more like this:
'templateOptions.class': function($viewValue, $modelValue, scope) {
if ($viewValue === 'abc') {
return 'some-class';
} else {
return 'some-other-class';
}
}

ExtJS bind to singleton object

I am trying to bind to value in defined class, to react on it change.
But no reaction happens. I extended object from data.Model, and trying to bind via links, but something doing wrong.
class with parameter:
Ext.define('Platform.core.OssStatusAdapter', {
extend : 'Ext.data.Model',
alternateClassName : [ 'OssStatusAdapter' ],
singleton : true,
fields : [ {
name : 'ossConected',
type : 'boolean',
defaultValue : false
} ]
});
Code in some class where value is changed:
OssStatusAdapter.set('ossConected',event.connected);
code where expect reaction to binding:
Ext.define('Plugin.scheduler.package.config.PackageConfigurationViewModel', {
extend : 'Ext.app.ViewModel',
...
links: {
ossAdapter: {
type: 'Platform.core.OssStatusAdapter',
create: true
}
},
...
formulas : {
canDeploy : {
bind : {
isOssUp : '{ossAdapter.ossConected}'
},
get : function(data) {
return data.isOssUp;
}
}
}
but in formula value is not changing.
What I missed?
Try to define your formula this way:
formulas: {
canDeploy: function(get) {
var connected = get('ossAdapter.ossConected');
// see if this moves when you change the record
console.log('connected', connected);
return connected;
}
}

Resources