Refresh table LWC - javascript

I've been trying to rerender a table when I update an Account record with a lightning-record-form.
I tried looking up a solution to this with similar questions I found here but I'm still not able to achieve this.
In this case I hardcoded the recordId with the Account with the name 'Gonzalo' shown in the preview below all the code. So the wanted result is to update the account name or any field and see the instant outcome in the table.
Here's my code:
Apex method (just in case):
#AuraEnabled(cacheable=true)
public static List<Account> getCuentas() {
return [SELECT id, Name, Phone FROM Account LIMIT 5];
}
Form (HTML):
<lightning-record-form
object-api-name="Account"
record-id="0015e00000F2JoWAAV"
fields={fields}
onsubmit={handleSubmit}
>
</lightning-record-form>
Table (HTML):
<lightning-datatable
key-field="pk"
data={cuentas}
columns={columnas}
onrowselection={action}
hide-checkbox-column
onrowaction={handleRow}
default-sort-direction={defaultSortDirection}
sorted-direction={sortDirection}
sorted-by={sortedBy}
onsort={onHandleSort}
>
</lightning-datatable>
Related code (JS):
***Imports***
import { refreshApex } from '#salesforce/apex';
import NAME from '#salesforce/schema/Account.Name';
import PHONE from '#salesforce/schema/Account.Phone';
import getCuentas from '#salesforce/apex/ProbandoJSON.getCuentas';
import { LightningElement, api, wire, track } from 'lwc';
***Vars for the form fields***
fields = [NAME, PHONE];
***Columns***
columnas = [
{
label: 'View',
type: 'button',
initialWidth: 75,
typeAttributes: {
label: {
fieldName: 'Boton'
},
title: 'Preview',
alternativeText: 'View',
variant: 'border-filled'
}
},
{
label: 'Name',
fieldName: 'Name',
sortable: true
},
{
label: 'Phone',
fieldName: 'Phone'
}
];
***Accounts***
#track cuentas = [];
_wiredResult;
#wire(getCuentas)
wireCuentas(result) {
this._wiredResult = result;
if(result.data) {
console.log('cuentas');
console.log(result.data);
for(var i in result.data) {
let obj = {};
obj.Id = result.data[i].Id;
obj.Name = result.data[i].Name;
obj.Phone = result.data[i].Phone;
obj.Boton = parseInt(i) + 1;
this.cuentas = [...this.cuentas, obj];
}
console.log('cuentas de nuevo');
console.log(this.cuentas);
} else if(result.error) {
console.log('error cuentas');
console.log(result.error);
}
}
***Submit handler for the Save button in the form***
handleSubmit(event) {
console.log('saving...')
return refreshApex(this._wiredResult);
}
Preview of the component:
Table
Form

Looks like a cache issue.
We can solve this in a couple of ways as follow:
Removing cacheable
You need to remove (cacheable=true) and then have to call the apex method imperatively on each form updates or while loading initial data.
Creating an unnecessary parameter in apex method which will be having new value on each call
You need to receive an additional parameter as a integer in the apex method, and then in lwc just create a var initializing it with 0, and on each update increment it by 1.
Then use same var to call the apex method via wire or in imperative calls.

Related

Vue not updating Vuetify data-table from array with data from Axios request

I have a problem and have been looking for hours to get it resolved, without any success.
In short: in a forEach loop I am adding my const "mijndata" to my array "Signalen". I can see that my array is filled successfully.
However, my data-table component from Vuetify is not showing my data. If I click the sort column or if I do a search, my data suddenly appears. So the problem must be that Vue doesn't detect changes to the "signalen" array.
I have already read multiple other threads on this issue and I have read: https://v2.vuejs.org/v2/guide/list.html#Caveats
But I am so far unable to resolve this issue.
Note: if I do a direct push into my "signalen" array from the results of my Axios calls, I don't have this problem. But unfortunately this is not an option as I am needing to create an object which is then pushed into the array.
Here is my problem in code:
mounted () {
axios.get('/my api url').then(response => {
const signalen = response.data.resources;
signalen.forEach((signaal) => {
const mijndata = {};
axios.get('/my api url').then(response => {
const relatedSensoren = response.data.resources;
relatedSensoren.forEach(relatedSensor => {
axios.get('/my api url').then(response => {
const sensorConfigs = response.data.resources;
sensorConfigs.forEach(sensorConfig => {
axios.get('/my api url').then(response => {
const alarmRegels = response.data.resources;
alarmRegels.forEach(alarmRegel => {
axios.all([axios.get('/my api url')])
.then(axios.spread((...responses) => {
mijndata.signaalid = signaal.ac_id;
mijndata.signaalsettingid = alarmRegel.ar_id;
}));
});
});
});
});
});
});
//A few more gets following the same logic as above...
//Push my const "mijndata" into my array "signalen"
this.signalen.push(mijndata)
});
});
//I can see that all of my objects are in my array "signalen"
window.console.log(this.signalen)
},
My data-table:
<v-data-table
:headers="headers"
:items="signalen"
:search="search"
class="elevation-1"
>
My array in my export:
export default {
data: () => ({
headers: [
{
text: 'Signaalnummer',
align: 'left',
value: 'signaalid',
},
{ text: 'Wanneer gaat dit alarm af?', value: 'signaalsetting' },
{ text: 'Manier van alarmeren', value: 'alarmsetting' },
{ text: 'Geadresseerde', value: 'alarmpersoon' },
{ text: 'Actions', value: 'action', sortable: false },
],
signalen: [],
}),
The headers match the keys from my array. Like I said, the problem is purely in pushing my "mijndata" object into my "signalen" array.
I would really appreciate any help!
Finally found the answer.
I fixed it by using Vue.set on the const "mijndata".
Eg: Vue.set(mijndata, 'signaalid', signaal.ac_id)

Dynamically passing JSON value to another template

I have one template, let's call it Template A that prints JSON data into a table, one column includes a button which is conditionally rendered when has_violations equals true.
An example of the table:
Table
What I want to accomplish is to take the driver_id that is associated with that particular row into the router link and have it passed onto a different template file let's call it Template B.
But how can I accomplish this using Vuex Store?
Sample JSON data:
{"driver_id":1,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"13","driver_trailer":"83","driver_status":"driving","has_violations":false},
{"driver_id":2,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"58","driver_trailer":"37","driver_status":"sleeping","has_violations":true},
{"driver_id":3,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"80","driver_trailer":"27","driver_status":"driving","has_violations":true},
Basic steps:
Get index of row on button click.
Get index of JSON data using value from Step 1.
Store the JSON data from Step 2 into Vuex.
Send user to Template B using router.
Retrieve data from Store when in Template B
Because you did not show your exact structure, the code below is just a basic structure.
Here's the code:
/* VUEX demo */
new Vuex.Store({
state: {
driver_data: undefined
},
mutations: {
recordDriver({ state }, payload){
state.driver_data = payload;
}
}
});
/* TEMPLATE A demo */
new Vue.component('template-a', {
data: function(){
return {
// Assume this is the JSON
driverJSON: [
{ driver_id: 1, driver_name: 'John Smith' },
{ driver_id: 2, driver_name: 'Bob John' }
]
};
},
methods: {
onButtonClicked: function(e){
const button = e.target;
const td = button.parentElement;
const tr = td.parentElement;
const indexOfTr = [...tr.parentElement.children].findIndex(row => row === tr);
const dataToStore = this.driverJSON[indexOfTr];
// Store data into $store
this.$store.commit('recordDriver', dataToStore);
// After storing, direct page using $router
this.$router.go({ ... });
}
}
});
/* TEMPLATE B demo */
new Vue.component('template-b', {
data: function(){
return {
// Get driver data using $store
driver: this.$store.state.driver_data
}
}
});
I like Yong's answer, but I would rather suggest you to pass the driverID as a prop to your route and then use a VueX getter to get the violations for the particular ID.

How setup valuegetter for auto Group Column in Ag-Grid

I have autoGroupColumnDef and I want to setup text filter. But values of the column come from getDataPath method. But I need another value in the filter.
autoGroupColumnDef: {
headerName: "Systems",
filter: 'text',
valueGetter: function(params) {
var result = params.data.hospName || params.data.hospitalSystem;
return result;
},
cellRendererParams: {
suppressCount: true,
innerRenderer: function(params) {
var result = params.data.hospName || params.data.hospitalSystem;
return result;
}
}
},
After trying on couple of things,
Option 1: You can make use of [filterParams][1]. This only helps to play around with options/choices in the filterMenu..
function filterCellRenderer(params) {
//other than params.value nothing else will be there..
// params.data won't be there when its called from filter popup
return params.value+" Custom";
}
var gridOptions= {
...,
treeData: true,
components: {
...,
filterCellRenderer: filterCellRenderer
},
autoGroupColumnDef: {
...,
filterParams: {
cellRenderer: 'filterCellRenderer',
//values: ["A", "XYZ"] //you can feed directly specific values. These need to be part of filePath. Else filtering won't work.
}
}
}
Option 2: If you are looking for custom filter (tweak with GUI), or you want to post processing after it has been configured by ag-grid you can define the following:
var gridOptions = {
...
getMainMenuItems: getMainMenuItems,// function to build your own menu
postProcessPopup: function(params){
// edit the popup..
//params.type gives whether its column menu or not.
//params.ePopup gives handler to popup which you can modifiy.
},
...
}
OR you can build your own custom filter as described here

how to save a SlickGrid column order (js)?

I have two grids in my application.
var columns1 = [
{
name: "Address",
field: "address"
id: "address",
sortable: true
}
]
var columns2 = [
{
{
name: "Rating, in %",
field: "rating"
id: "rating_percent",
resizable: false
}
]
They are absolutely independent from each other. Also, I have some grid events descriptions in another js file.
grid.onColumnsReordered.subscribe(function (e, args) {
_this.updateHeaderRow();
// here
});
When user changes the columns order, then I want to save this order. Should I change (overwrite) the DOM elements, I mean column1 and column2?
So question: how can I save the columns order?
njr101's answer (using store.js) is great, but note: store.js cannot store functions (i.e. for editors, formatters), so once you store.get() the columns, you'll need to add the functions back, using your original stock "columns" array:
if (store.get('gridColumns')) {
grid.setColumns(store.get('gridColumns')); // Restore settings if available
grid.getColumns().forEach(function(ch) { // Re-create editor and formatter functions
var result = $.grep(columns, function(e){ return e.id == ch.id; });
if (result[0]) {
ch.editor = result[0].editor;
ch.formatter = result[0].formatter;
}
});
}
I have done this before and the easiest way I found was to store the columns in local storage. I use the store.js library which makes this pretty simple.
grid.onColumnsReordered.subscribe(function (e, args) {
store.set('gridColumns', grid.getColumns());
});
When you want to restore the columns (e.g. when the user returns to the page) you can just call:
grid.setColumns(store.get('gridColumns'));

YUI datatable error

I'm trying to put together an application which uses YUI's DataTable component but I get the "Data error" message. The datasource is configured to get the records from an ASP.NET web method. The records are returned to the client side successfully (I checked it with IE's debugger). My code looks like the following:
YAHOO.example.Basic = function() {
var dsWS_Restaurants = new YAHOO.util.DataSource("/DemoWebSite/RestaurantsWebService.asmx/GetList", { connMethodPost: true });
dsWS_Restaurants.connMgr = YAHOO.util.Connect;
dsWS_Restaurants.connMgr.initHeader('Content-Type', 'application/json; charset=utf-8', true);
dsWS_Restaurants.responseType = YAHOO.util.DataSource.TYPE_JSON;
dsWS_Restaurants.doBeforeParseData =
function(oRequest, oFullResponse, oCallback) {
// checked here if oFullResponse contains the desired results and it does.
}
dsWS_Restaurants.responseSchema =
{
resultsList: 'd.records',
fields: ["id", "name"]
};
var dsWS_Restaurants_ColumnDefs = [
{ key: "id", sortable: true, resizeable: true },
{ key: "name", sortable: true, resizeable: true }
];
var dsWS_Restaurants_DataTable =
new YAHOO.widget.DataTable("basic4", dsWS_Restaurants_ColumnDefs, dsWS_Restaurants, { caption: "dsWS_Restaurants" });
return {
oDS: dsWS_Restaurants,
oDT: dsWS_Restaurants_DataTable
};
} ();
...
Web method look like this:
public Object GetList() {
var restaurants =
new []{
new
{
id="1",
name="Popeyes spinach"
},
new
{
id="2",
name="Big pappas cottage"
}
};
return restaurants.Select (x => new { id = x.id, name = x.name });
}
Any help is welcome and appreciated. Thanks in advance.
I believe that the overridable doBeforeParseData method should return the oFullResponse object...
dsWS_Restaurants.doBeforeParseData =
function(oRequest, oFullResponse, oCallback) {
// checked here if oFullResponse contains the desired results and it does.
return oFullResponse;
}
.. but there may be more to it than just that.
I found out what caused the error. In the responseSchema of the datasource the resultList was defined as 'd.records' but I had no "records" field returned by the web method. I replaced 'd.records' with 'd' and the sample worked. My mistake was that I borrowed the code from a sample application from http://mattberseth.com/blog/2008/09/dynamic_data_experimenting_wit.html which used the "records" field.
Happy coding.

Categories