Related
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>
I am having trouble refreshing a Datatable in my Lightning Web Component after updating a record. I am calling an onclick action on a button within the row, and imperatively calling an Apex method to update that record. I then call the refreshApex() to update the data being fed into the Datatable.
However, after the refreshApex(), the tables within the for:each are not being refreshed with new data.
The records are properly modified and reflect the changes properly when refreshing the entire page.
Note: The Task object is not supported in LWC, and I cannot use the updateRecord() method to update these records.
HTML:
<template>
<template if:true="{taskCompWrapperList}">
<!--<lightning-layout multiple-rows="false" pull-to-boundary="small">-->
<template for:each="{taskCompWrapperList}" for:item="taskTemplate">
<lightning-layout-item
key="{taskTemplate.taskSectionOrder}"
size="3"
class="slds-p-around_x-small"
>
<!-- Start bear tile -->
<lightning-card title="{taskTemplate.taskSectionTitle}">
<div class="slds-m-around_medium">
<template if:true="{taskTemplate.taskList}">
<lightning-datatable
key-field="Id"
data="{taskTemplate.taskList}"
onrowaction="{handleRowAction}"
columns="{columns}"
onsave="{handleSave}"
draft-values="{draftValues}"
>
</lightning-datatable>
</template>
<template if:true="{contact.error}">
<!-- handle Apex error -->
</template>
</div>
</lightning-card>
<!-- End bear tile -->
</lightning-layout-item>
</template>
<!--</lightning-layout>-->
</template>
</template>
Javascript:
import { LightningElement, api, wire ,track} from 'lwc';
import getTaskCompWrappers from '#salesforce/apex/ENT_Task_Utility.getTaskComponentWrapper';
import updateTask from '#salesforce/apex/ENT_Task_Utility.updateTask';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { updateRecord } from 'lightning/uiRecordApi';
import { refreshApex } from '#salesforce/apex';
const COLS = [
{
type: 'button',
label: 'Complete',
typeAttributes:
{
//iconName: 'action:preview',
label: 'Complete',
name: 'Complete',
title: 'Complete',
value: 'Complete',
variant: 'brand',
alternativeText: 'Complete'
}
},
{
type: 'button-icon',
label: 'Start',
typeAttributes:
{
iconName: 'action:approval',
//label: 'Complete',
name: 'Start',
title: 'Start',
value: 'Start',
variant: 'success',
alternativeText: 'Start',
}
},
{
type: "button",
typeAttributes:
{
label: 'View',
name: 'View',
title: 'View',
disabled: false,
value: 'view',
iconPosition: 'left'
}
},
{
type: "button",
typeAttributes:
{
label: 'Edit',
name: 'Edit',
title: 'Edit',
disabled: false,
value: 'edit',
iconPosition: 'left'
}
},
//{ label: 'Complete', fieldName: 'Task_Complete__c', editable: true },
{ label: 'Status', fieldName: 'Status', type: 'picklist', editable: true },
{ label: 'Completed', fieldName: 'Completed', type: 'boolean', editable: true },
{ label: 'Owner', fieldName: 'OwnerId', editable: true },
{ label: 'Subject', fieldName: 'Subject' },
{ label: 'Due Date', fieldName: 'ActivityDate', type: 'date' }
];
export default class ENT_Task_Utility_LWC extends LightningElement {
#api objApiName;
#api recordId;
#track testMessage = 'Test Failed :c';
#track error;
#track columns = COLS;
#track draftValues = [];
taskCompWrapperList;
#track error;
//#wire(getTasks, {recordId: '$recordId'}) taskList;`
#wire(getTaskCompWrappers, {recordId: '$recordId', objApiName: '$objApiName'})
taskCompWrapperListWire({ error, data }) {
if (data) {
this.taskCompWrapperList = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.taskCompWrapperList = undefined;
}
}
updateTaskValues (taskId, taskStatus) {
// eslint-disable-next-line no-console
console.log('updateTaskValues hit');
for(var counter = 0; counter < this.taskCompWrapperList.length; counter++) {
// eslint-disable-next-line no-console
console.log('taskWrapper: ' + this.taskCompWrapperList[counter]);
for(var counter2 = 0; counter2 < this.taskCompWrapperList[counter].taskList.length; counter2++) {
// eslint-disable-next-line no-console
console.log('task: ' + this.taskCompWrapperList[counter].taskList[counter2]);
if(this.taskCompWrapperList[counter].taskList[counter2].Id == taskId)
{
this.dispatchEvent(
new ShowToastEvent({
title: 'Task Id Found!',
message: this.taskCompWrapperList[counter].taskList[counter2].Id,
variant: 'success'
})
);
this.taskCompWrapperList[counter].taskList[counter2].Status = taskStatus;
}
}
}
}
handleRowAction(event) {
//TODO
}
}
Apex methods:
#AuraEnabled(cacheable=true)
global static List<Task> getTasks(String recordId)
{
return [SELECT Id, Subject, OwnerId FROM Task WHERE WhatId = :recordId];
}
#AuraEnabled(cacheable=true)
global static List<ENT_Task_Comp_Wrapper> getTaskComponentWrapper(String recordId, String objApiName)
{
List<Task_Template__c> taskTemplateList = [SELECT Id, Task_Component_Section_Order__c, Task_Component_Section_Title__c, (SELECT Id FROM Task_Template_Items__r)
FROM Task_Template__c
WHERE Active__c = true AND sObject__c = :objApiName ORDER BY Task_Component_Section_Order__c ASC];
List<Task> taskList = [SELECT Id, Task_Template_Item__c, OwnerId, Owner.Name, Subject, Description, Status, ActivityDate, Task_Complete__c FROM TasK WHERE WhatId = :recordId];
List<ENT_Task_Comp_Wrapper> taskCompWrapperList = new List<ENT_Task_Comp_Wrapper>();
for(Task_Template__c taskTemplate : taskTemplateList)
{
ENT_Task_Comp_Wrapper taskCompWrapper = new ENT_Task_Comp_Wrapper();
taskCompWrapper.taskSectionTitle = taskTemplate.Task_Component_Section_Title__c;
taskCompWrapper.taskSectionOrder = (Integer)taskTemplate.Task_Component_Section_Order__c;
taskCompWrapper.taskList = new List<Task>();
for(Task currentTask : taskList)
{
for(Task_Template_Item__c taskTemplateItem : taskTemplate.Task_Template_Items__r)
{
if(taskTemplateItem.Id == currentTask.Task_Template_Item__c)
{
taskCompWrapper.taskList.add(currentTask);
}
}
}
taskCompWrapperList.add(taskCompWrapper);
}
System.debug(taskCompWrapperList);
return taskCompWrapperList;
}
#AuraEnabled
global static void updateTask(String taskId, String newStatus)
{
System.debug(taskId);
Task taskToUpdate = new Task(Id = taskId, Status = newStatus);
update taskToUpdate;
//update taskToUpdate;
}
#AuraEnabled
global static void updateTask(String taskId, String newStatus)
{
System.debug(taskId);
Task taskToUpdate = new Task(Id = taskId, Status = newStatus);
update taskToUpdate;
//update taskToUpdate;
}
In your JS code you have imported refreshApex
by using this line import { refreshApex } from '#salesforce/apex';
but you didn't assigned to any wire method. Hence data is not refreshed
Please refer this documentation.
To refresh a wired method, pass the argument the wired method receives (which is the wired value) to refreshApex(). In this sample code, the wired method is taskCompWrapperListWire. Hold on to the value provisioned by the wire service and pass it to refreshApex().
#wire(getTaskCompWrappers, {recordId: '$recordId', objApiName: '$objApiName'})
taskCompWrapperListWire({ error, data }) {
if (data) {
this.taskCompWrapperList = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.taskCompWrapperList = undefined;
}
}
And then use refreshApex() as below:
refreshApex(this.taskCompWrapperListWire);
Update you code as below
updateTaskValues({
taskId: this.taskId,
taskStatus: this. taskStatus
})
.then(() => {
// your code logic
refreshApex(this.taskCompWrapperListWire);
})
.catch((error) => {
this.message = 'Error received: code' + error.errorCode + ', ' +
'message ' + error.body.message;
});
you probably need to wait for next release to have a correct way to handle such situation.
You are getting record through uiRecordApi and updating through Apex if I'm correct.
Then you would need to use getRecordNotifyChange() available in Winter 21 release.
Apart from the answer provided by Sudarshan, you should also define taskCompWrapperList as a reactive property to make it rerender when the property is updated.
#track taskCompWrapperList = [];
I have a pivot grid which display if the users have create,read,update,delete" permissions, the users are agrouped in this way departaments > estabilishments > sectors > users and i want the user to be able to edit this fields.
I already tried using with a renderer:
aggregate: [{
renderer: function(value, record, dataIndex, cell, column) {
var id = Ext.id();
Ext.defer(function() {
Ext.widget('checkbox', {
renderTo: id,
checked: value,
listeners: {
change: {
fn: function(event, target) {
//some function here
}
}
}
});
}, 100);
return Ext.String.format('<div id="{0}"></div>', id);
},
aggregator: 'aggCheckBoxR',
dataIndex: 'Incluir',
header: 'Incluir'
}]
and with a widget column:
aggregate: [{
aggregator: 'aggCheckBoxR',
column: {
xtype: 'widgetcolumn',
widget: {
xtype: 'checkbox',
listeners: {
change: function(cb) {
//some function here
}
}
}
},
dataIndex: 'Incluir',
header: 'Incluir'
}]
My Aggregator:
aggCheckBoxR: function(records, measure, matrix, rowGroupKey, colGroupKey) {
if (records.length > 1) {
let checkAllTrue = true;
for (var i = 0; i < records.length; i++) {
if (records[i].get('Incluir') === false || records[i].get('Incluir') === 0) {
checkAllTrue = false;
}
}
return checkAllTrue;
} else {
return records[0].get('Incluir');
}
}
The checkbox apears on the grid but my problem is the data "dont persist", whenever i expand or collapse a left axis on the pivot grid the value of the checkbox returns to its original value, how can i persist this data?
Already tried update the record manualy
change: function(cb) {
var nomeCmp = cb.getWidgetRecord().data._compactview_;
Ext.getStore('acesso.ColabStore').findRecord('Nome', nomeCmp).data.Incluir = true;
}
But still, it doestn't work.
EDIT: Had to change the column object event listener to:
{
xtype: 'widgetcolumn',
widget: {
xtype: 'checkbox',
listeners: {
afterrender: function(component, eOpts) {
console.log('after,render');
component.getEl().on('change', function(e, el) {
console.log('change func here');
})
}
}
}
}
With this, the change event is only fired when the users check a checkbox, and finally, I could use
norbeq's answer
You can update the record manually using:
record.set('Incluir',true);
and if you dont wan't to send changes to server:
record.commit();
Actually I'm going to implement a tree view, where the user should have the option to reorder the structure with drag and drop. Actually I can't figure out how to enable drag and drop. I found a lot of examples using the 'treeviewdragdrop' plugin, which is just working with the classic toolkit.
The following Code made me move the first node but not more.
this.toParentSource = new Ext.drag.Source({
element: this.getView().element.down('.x-gridcell'),
constrain: {
element: this.getView().body,
vertical: true
}
});
Can you help me with this problem? I'm using ExtJS 6.5.2 modern toolkit.
This is how I enabled drag and drop for trees in modern Ext JS:
First I've written a plugin which creates the sources that should be draggable.
Plugin
Ext.define('test.component.plugin.TreeDragger', {
extend: 'Ext.AbstractPlugin',
alias: 'plugin.treedrag',
mixins: ['Ext.mixin.Observable'],
constructor: function (config) {
this.mixins.observable.constructor.call(this, config);
},
init: function (component) {
var me = this;
this.source = new Ext.drag.Source({
element: component.element,
handle: '.x-gridrow',
constrain: {
element: true,
vertical: true
},
describe: function (info) {
var row = Ext.Component.from(info.eventTarget, component);
info.row = row;
info.record = row.getRecord();
},
proxy: {
type: 'placeholder',
getElement: function (info) {
console.log('proxy: getElement');
var el = Ext.getBody().createChild({
style: 'padding: 10px; width: 100px; border: 1px solid gray; color: red;',
});
el.show().update(info.record.get('description'));
return el;
}
},
// autoDestroy: false,
listeners: {
scope: me,
beforedragstart: me.makeRelayer('beforedragstart'),
dragstart: me.makeRelayer('dragstart'),
dragmove: me.makeRelayer('dragmove'),
dragend: me.makeRelayer('dragend')
}
});
},
disable: function () {
this.source.disable();
},
enable: function () {
this.source.enable();
},
doDestroy: function () {
Ext.destroy(this.source);
this.callParent();
},
makeRelayer: function (name) {
var me = this;
return function (source, info) {
return me.fireEvent(name, me, info);
};
}
});
Next I used this plugin inside my tree.
Tree
xtype: 'tree',
hideHeaders: true,
plugins: {
treedrag: {
type: 'treedrag',
listeners: {
beforedragstart: function (plugin, info) {
// logic to identify the root and prevent it from being moved
console.log('listeners: beforedragstart');
}
}
}
},
columns: [{
xtype: 'treecolumn',
flex: 1,
}
]
Then I defined the drop targets inside the controller.
Controller
afterLoadApportionmentObjectsForTree: function (succes) {
if (succes) {
tree = this.getView().down('tree');
if (tree) {
tree.expandAll();
tree.updateHideHeaders(tree.getHideHeaders());
var store = tree.getStore();
store.remoteFilter = false;
store.filterer = 'bottomup';
this.createDropTargets();
}
}
},
createDropTargets: function () {
var me = this,
rows = tree.innerItems;
Ext.each(rows, function (el) {
var target = new Ext.drag.Target({
element: el.element,
listeners: {
scope: me,
drop: me.onDrop,
beforeDrop: me.onBeforeDrop
}
});
});
},
onDrop: function (target, info, eOpts) {
var source = info.record,
row = Ext.Component.from(target.getElement(), tree),
destination = row.getRecord(),
parentNode = source.parentNode;
destination.appendChild(source);
destination.expand();
if (!parentNode.hasChildNodes()) {
parentNode.set('leaf', true);
}
},
onBeforeDrop: function (target, info, eOpts) {
var source = info.record,
row = Ext.Component.from(target.getElement(), tree),
destination = row.getRecord();
// prevent the user to drop the node on itself
// this would lead to an error caused by recursive method calls
if (source == destination) {
return false;
}
// prevent the user to drop a node on it's children
// this would lead to an error caused by recursive method calls
if (source.findChild('number', destination.get('number'), true) != null) {
return false;
}
return true;
}
Background
Had upgraded the typescript to 2.6.2 from 2.3.4. Ag grid have compile issue so its upgraded to 15.0.0. After upgrade existing pagination code for ag-grid is not working.
Previous Configuration - ag-grid and pagination works fine
on click search button from form searchCategory() method will be called and load the grid
package.json
"ag-grid": "^8.1.0",
"ag-grid-angular": "^8.1.0",
.....
"typescript": "^2.3.4"
temp.ts
gridOptions = <GridOptions>{
context: {},
paginationPageSize: AppUtils.IR_PAGINATION_SIZE,
/* rowModelType: 'pagination',*/
rowModelType: 'infinite',
pagination: true,
enableServerSideSorting: true,
suppressDragLeaveHidesColumns: true,
onGridSizeChanged: () => {
this.gridOptions.api.sizeColumnsToFit();
},
getRowHeight: () => {
return 32;
},
components: {
getTypeDesc : function(params: any) {
var eDiv = document.createElement('div');
let desc = params.context.typeMaster.filter(function (item: any) {
if (params.data.typeCode === item.typeCode) {
return item.typeDescription;
}
});
eDiv.innerHTML = '<span>' + desc[0].typeDescription + '</span>';
return eDiv;
},
};
typeMaster: TypeMasterModel[];
categoryMaster: CategoryModel[];
category: CategoryModel = new CategoryModel();
severityMaster: SeverityMasterModel[];
selectedRowsValue: number[];
columnDefs: any[] = [
{ headerName: '', field: 'catCode', hide: true },
{ headerName: 'Category', field: 'catDesc', width: 550 },
{ headerName: 'Type', field: 'typeCode', cellRenderer:'getTypeDesc' }
{ headerName: 'PatientID', field: 'patIdMandYn' },
{ headerName: 'EquipmentID', field: 'equipIdMandYn' },
{ headerName: 'WorkorderId', field: 'workOrderMandYn' }
];
action: string = '';
searchCategory() {
let self = this;
let dataSource = {
rowCount: null, //
getRows: (params: any) => {
this.http.get(//server call ).subscribe(res => {
let result = res['result'];
if (result != null && result.paginatedList != null) {
this.totalRecords = result.paginatedList.length;
if (this.totalRecords <= 0) {
this.gridStatusMessageDisplay("");
}
params.successCallback(result.paginatedList, result.totalRecords);
} else {
this.gridStatusMessageDisplay("");
}
});
}
}
this.gridOptions.api.setDatasource(dataSource);
}
temp.html
New Configuration Details
package.json
ag-grid": "^15.0.0",
"ag-grid-angular": "^15.0.0",
"typescript": "^2.6.2"
test.ts pagination replaced with infinite.
/* rowModelType: 'pagination',*/
rowModelType: 'infinite',
pagination: true,
Current Behavior
On call to searchCategory(), server call is made and data loaded into grid with pagination bar.
On clicking next in the pagination bar, its not calling the registered data source and halt there.
Expected Behaviour
pagination should work properly. Datasource should be called on next & prev and update the grid
use other pagination libraries to solve this issue, Here primeng pagination,
https://www.primefaces.org/primeng/#/paginator
//html
<div *ngIf="totalRecords > catPaginationSize">
<p-paginator rows="10" totalRecords="{{totalRecords}}" (onPageChange)="paginate($event)" ></p-paginator>
</div>
//ts
import { PaginatorModule } from 'primeng/primeng';
paginate(event: any) {
this.startIndex = event.first;
this.rowsPerPage = event.rows;
this.paginatedList();
}