How to disabled a button on lightning datatable based on field value? - datatable

I want to disabled a button on a lightning datatable based on a boolean field, I came to this solution:
const columns = [
{ label: 'Client', type:'button',
typeAttributes: {
label: 'Client',
disabled: {fieldName: 'Client__c' }
} }
];
The problem is, I need to make visible when is true, but it actually doing the opposite, i search to a enabled property or trying something like this:
Client__c == true ? false : true;
but it doens't work..
I also try this solution here
this.tableData = result.data.map((value) => ({ ...value, clientDisabled: ('Client__c' == true) ? false : true }));
And the column:
const columns = [
{ label: 'Client', type:'button',
typeAttributes: {
label: 'Client',
disabled: {fieldName: 'clientDisabled' }
} }
];
Also, not work, all buttons became disabled.
Also, I would like to put a - when is disabled (and the field = false), like this:

'Client__c' == true is always false; you're comparing two literal values that will never be equal. You'll want to use the data from the record instead:
this.tableData = result.data.map((record) => ({ ...record, clientDisabled: !record.Client__c }));

Related

Cypress conditional statement for a disabled button element

Trying to click a button based on another element with enabled/disabled status. For some reason, my disabled check code is not working and it always ends in another statement ('No existing routes found') even though the UI has a select button enabled.
cy.get('voyage-suggested-routes')
.find('button.selectButton')
.then(($routes) => {
if ($routes.is(":disabled")) {
cy.log("No existing routes found...")
} else {
cy.log("Deleting......")
cy.get('.delete-button').click({ force: true, multiple: true })
}
});
This is the DOM: (There are 3 elements by default and a delete option will be there for each Select button if it is not disabled.)
<button class="selectButton" disabled route="1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="..."></path></svg>
SELECT
</button>
Tried the jquery method as well but the same result.
var btnStatus = Cypress.$('.selectButton')
.is(":disabled");
if (btnStatus == true) {
cy.log("Deleting......")
cy.get('.delete-button').click({ force: true, multiple: true })
} else {
cy.log("No existing routes found...")
}
What am I missing?
Update 1:
After Electron's input, my new code is:
cy.get('voyage-suggested-routes')
.find('button.selectButton')
.then(($routes) => {
if ($routes.is(":disabled").length === 0) {
cy.log("No existing routes found...")
} else {
cy.log("Deleting......")
cy.get('.delete-button').click({ force: true, multiple: true })
}
});
From the docs jQuery .is()
Description: Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.
So if only one route is disabled, the delete will not go ahead.
Try using a filter to see if any are disabled.
cy.get('voyage-suggested-routes')
.find('button.selectButton')
.then(($routes) => {
const disabled = $routes.filter(":disabled")
if ($routes.length === disabled.length) {
cy.log("No existing routes found...")
} else {
cy.log("Deleting......")
cy.get('.delete-button').click({ force: true, multiple: true })
}
})
It's because you need each instead of then, like this:
.each(($routes) => {
in order to perform as many actions as there are button elements.
Edit: as Electron stated in the comments, the suggestion below will fail a test if all buttons are disabled, so take care if you use it.
And to better optimize your code, your can set the selector as .find('button.selectButton:not(:disabled)') then you don't need if block at all, just the delete statement.
Here's a custom command which conditionally runs a callback, depending on the result of filtering by given selector.
Not a lot of difference to .then(($routes) => { const disabled = $routes.filter(":disabled") pattern. Unfortunately ending part of a chain is quite difficult, as the whole test is considered one chain.
Cypress.Commands.add('maybe', {prevSubject:true}, (subject, selector, callback) => {
const result = subject.filter(selector)
if (result.length > 0) {
cy.log(`Maybe: Found ${result.length} "${selector}"`)
cy.wrap(result).then(callback)
return
}
cy.log(`Maybe: Not found: "${selector}"`)
})
cy.get('button.selectButton')
.maybe(':not(:disabled)', ($result) => {
// can use result of filter here
console.log('result is', $result))
// or conditionally run some other commands
cy.log(`Deleting...`)
cy.get('.delete-button').click({ force: true, multiple: true })
})
// runs either way
cy.wrap('something')
.then(x => console.log('next', x))

Immutable.js update a value multiple key in Map

I am trying to use Immutable.js. So I used the Map object and I have a 2 fields for ex.
const initialState = Map({
isUserAuthorized : false,
pending : false
});
and I want to update both. How I can do that? I tried to use a standard Update method like that:
state.update( "isUserAuthorized", () => true, "pending", () => false);
But it isn't working somehow. So I have only one idea - update only particular key and after that do the same with other and return a result.
But I think it is a not so perfect idea. Maybe other normal variants exist? Thanks for any help!
P.S. I found that it can be done via set and withMutations like:
initialState.withMutations(map => {
map.set("isUserAuthorized", true).set("pending", false);
})
But is it really so hard to update multiple values in Map?
You can use .set():
const initialState = Map({
isUserAuthorized : false,
pending : false
});
initialState = initialState.set('isUserAuthorized', true);
initialState = initialState.set('pending', false);
If you don't want to repeat it. You can create a function to pass multiple params.
Another way is with .merge():
const newState = initialState.merge({
isUserAuthorized: true,
pending: false
});
Or chaining multiple sets:
initialState = initialState.set('isUserAuthorized', true)
.set('pending', false)
.set('key3', 'value3');
Bit of a stale post but in case this helps anyone else, I just had to figure this out yesterday. Here is what I ended up doing.
If you want to change all the keys to be the same then you can map.
const initialState = Map({
isUserAuthorized : false,
pending : false
});
const newState = initialState.map(() => true)
// { isUserAuthorized: true, pending: true }
If there's specific keys you want to set then add a condition or ternary expression in your map and only update what's relevant. Here's a quick and dirty example.
const initialState = Map({
isUserAuthorized : false,
pending : false,
dontChangeMe: false
});
const newState = initialState.map((val, key) => (
key === 'isUserAuthorized' || key === 'pending' ? true : value
)
// { isUserAuthorized: true, pending: true, dontChangeMe: false }
If you want to get clever and make things reusable you could make a curried utility to pass in a list of the target keys, then just invoke with the value you want them to be.

Mongoose conditional required validation

I'm using mongoose and trying to set a custom validation that tells the property shall be required (ie. not empty) if another property value is set to something. I'm using the code below:
thing: {
type: String,
validate: [
function validator(val) {
return this.type === 'other' && val === '';
}, '{PATH} is required'
]}
If I save a model with {"type":"other", "thing":""} it fails correctly.
If I save a model with {"type":"other", "thing": undefined} or {"type":"other", "thing": null} or {"type":"other"} the validate function is never executed, and "invalid" data is written to the DB.
As of mongoose 3.9.1, you can pass a function to the required parameter in the schema definition. That resolves this problem.
See also the conversation at mongoose: https://github.com/Automattic/mongoose/issues/941
For whatever reason, the Mongoose designers decided that custom validations should not be considered if the value for a field is null, making conditional required validations inconvenient. The easiest way I found to get around this was to use a highly unique default value that I consider to be "like null".
var LIKE_NULL = '13d2aeca-54e8-4d37-9127-6459331ed76d';
var conditionalRequire = {
validator: function (value) {
return this.type === 'other' && val === LIKE_NULL;
},
msg: 'Some message',
};
var Model = mongoose.Schema({
type: { type: String },
someField: { type: String, default: LIKE_NULL, validate: conditionalRequire },
});
// Under no condition should the "like null" value actually get persisted
Model.pre("save", function (next) {
if (this.someField == LIKE_NULL) this.someField = null;
next()
});
A complete hack, but it has worked for me so far.
Try adding this validation to the type attribute, then adjust your validation accordingly. E.g.:
function validator(val) {
val === 'other' && this.thing === '';
}
thing: {
type: String,
required: function()[{
return this.type === 'other';
}, 'YOUR CUSTOM ERROR MSG HERE']
}

Select a row in jqGrid based on cell value

colModel: [
{ name: 'Id', index: 'Id', hidden: true, search: false },
{ name: 'Name', index: 'Name', hidden: true, search: false },
]
Just as the setSelection method allows selection of a row in jqGrid based on the row number, is it possible to similarly select a row based on one of the cell values.
For e.g. in the colModel above, is it possible to select a row having a certain 'Id' or 'Name' value...assuming that these values are unique for each row.
In the loadComplete: portion of your jqGrid you could iterate over each row and test for the value you are looking for. If the value is found, select the row.
Ex
loadComplete: function () {
var rowIds = $(this).jqGrid('getDataIDs');
for (i = 1; i <= rowIds.length; i++) {
rowData = $(this).jqGrid('getRowData', i);
if (rowData['Id'] == idSearchValue ) {
$(this).jqGrid('setSelection',i);
} //if
} //for
...
There would also be the rowattr: but I can't seem to find where you can get the rowID of the current row. Oleg might see this and respond as well as it was his addition to jqGrid but I didn't have any luck with my testing or read though of where you would get the current rowId to pass to the setSelection method.
If you have an array of objects containing cells values, you will need another approach.
Eg. with your colModel, you would like to retrieve rows with these values:
let toSearch = [
{ Name: 'Arthur', Id: 150},
{ Name: 'Julien', Id: 90},
]
Then what you can do is to retrieve the whole data from the jqGrid table and seek after values dynamically:
let data = $grid.jqGrid('getGridParam', 'data');
let toSelect = data.filter((row,i) => {
return toSearch.some(toSelectRow => {
for(let prop in prevRow) {
if(prevRow[prop] != row[prop])
return false;
}
return true;
})
});
toSelect.forEach((row) => $grid.jqGrid('setSelection',row.id, false)); // Prevent firing onSelectRow event
Here we pass false to prevent from firing onSelectRow event, if you need it just remove false

jqGrid change search filters on submit

I would like to alter the search filters after a user has submitted them. Normally jqGrid returns name in colmodel as the value for field, I would like to change this behavior for a specific column:
I would like to change:
{"groupOp":"AND","rules":[{"field":"available","op":"eq","data":"true"}]}
to
{"groupOp":"AND","rules":[{"field":"s.trait.available","op":"eq","data":"true"}]}
I have tried altering the submitted form in the ways below; firebug shows that the functions are never being called.
var searchOptions = {
multipleSearch:true, multipleGroup:false, closeOnEscape:true, closeAfterSearch:true,
sopt:['ge', 'eq', 'le'],
beforeSubmit:function (params, postdata) {
//alterations would be here
}
,
onclickSubmit:function (params, postdata) {
//alterations would be here
}
}
This approach works for editOptions and delOptions, I am not sure why I cannot get this to work for searching.
If you use the searching toolbar you can use beforeSearch callback to modify the postData.filter. In case of Singe Field searching or Advanced Searching you can use onSearch.
In the answer you can see how the postData.filter can be modified.
UPDATED: You did something wrong in your tests. The only problem is that the current implementation of searching don't initialize this to the grid, but it's not explicitly documented somewhere.
I created the demo for you which demonstrate that you do can modify the filter before relaoding of the grid will be started. If you would search in the grid for 'Client' equal to 300 the search request will be modified to 'amount' equal to 300 and you would see the results
The corresponding code is
$('#list').jqGrid('navGrid', '#pager', {add: false, edit: false, del: false}, {}, {}, {},
{
multipleSearch: true,
overlay: 0,
onSearch: function () {
var i, l, rules, rule, $grid = $('#list'),
postData = $grid.jqGrid('getGridParam', 'postData'),
filters = $.parseJSON(postData.filters);
if (filters && typeof filters.rules !== 'undefined' && filters.rules.length > 0) {
rules = filters.rules;
for (i = 0; i < rules.length; i++) {
rule = rules[i];
if (rule.field === 'name') {
// make modifications only for the 'contains' operation
rule.field = 'amount';
}
}
postData.filters = JSON.stringify(filters);
}
}});

Resources