Get current sort options when sort changes in vuetify data tables - vuetify.js

Our tables pagination and sorting are all done on the server, but I still want to allow the user to click column headers to sort. I need to access the tables sort options when the sorting changes. It seems the events are split
update:sort-by
update:sort-desc
I could create two methods, have the first event set the column (sort-by) value, and then the second method actually trigger the sort. But that sounds horrible and prone to race conditions or future bugs.
It would be better if the sort-by included the desc/asc data as well. I tried creating a ref to the table, but for some reason the properties containing the sort information are empty.
The event update:options contains all the necessary information, but that could fire for reasons other than sorting which isn't ideal either.
So I'm not sure if I'm missing something here. Is there a better way to accomplish this?
<v-data-table
ref="contractItemTable"
:headers="headers"
:items="contracts"
:disable-sort="isLoadingPage"
:server-items-length="tableTotal"
disable-pagination
hide-default-footer
#click:row="navigateToContract"
single-select
#update:sort-by="sortTable"
#update:sort-desc="sortTable"
item-key="id.id">
And the JS
public sortTable(event) {
console.debug(this.$refs.contractItemTable.sortBy);
console.debug(this.$refs.contractItemTable.sortDesc);
}
No matter what I click, the sortBy and sortDesc properties are empty. If I use the options event, the event contains the correct sortBy and sortDesc. But as stated above, not exactly the event I want to use. I have it working, but I have to "ignore" the initial load options event since it's not a valid time for this method to fire.

In data table add attribute:
:sort-desc.sync="sort_desc"
In data:
sort_desc:true,//or what you need by default
In observable:
sort_desc:function(val,_prev){
//do what you need to change sort and refresh
},

You're mostly there (and found your question while trying to sort out the best way to apply sorting to derived/computed column-items myself) ... just need to bind the v-data-table sort-by and sort-desc attributes to data elements so that you can refer to them in your sortTable function:
<v-data-table
...
:items="contracts"
:headers="headers"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
#update:sort-by="sortTable"
#update:sort-desc="sortTable"
...
>
export default {
data() {
contracts: [],
sortBy: 'contractDate', // make sure this matches headers[].value
sortDesc: false, // and both match your initial from-server sort
...
headers: [
{ text: 'Contract Date', sortable: true, value: 'contractDate' },
{ text: 'Contract Value', sortable: true, value: 'contractValue' }
...
]
},
methods: {
sortTable() {
if(this.sortBy === 'contractDate') {
this.contracts.sort( (a,b) => {
return ( a.contractDate > b.contractDate ? 1 : -1 ) * (this.sortDesc ? -1 : 1 );
});
}
if(this.sortBy === 'contractValue') {
this.contracts.sort( (a,b) => {
return ( a.contractValue > b.contractValue ? 1 : -1 ) * (this.sortDesc ? -1 : 1 );
});
}
}
}
}
Your sort logic obviously will vary, but included the Array.sort() callback example there to show where this.sortDesc is used to reverse the evaluation.
A big caveat to be aware of is that if you do not specify
<v-data-table :must-sort="true" >
Then the bound #update:sort-by and #update:sort-desc events will BOTH fire when sort is deactivated which happens when user clicks the same column header a third time (it cycles through sorted to no-sort states.)
To get around this, add something like
sortPending: false
to your data state, and when the event fires, return immediately out of the event if this.sortPending === true, and move your sort logic into a Vue.nextTick() callback:
methods: {
sortTable() {
if(this.sortPending) return;
this.sortPending = true;
this.$nextTick( () => {
this.sortPending = false;
/* your sort logic here */
});

Related

How can I reset the sort order after a row has been selected?

I am using smart-table to show a list of possible matches. When a row is selected, it then shows the next list of possibilities.
By default, the table is sorted by a "ranking" column. Its possible for the user to click a different header and have it sorted by that column instead.
After a row has been selected, and the next results are loaded, is it possible to reset the st-sort back to what it was initially? I've searched the github for terms like "reset sort" and I haven't been successful.
I worked it out.
Use this directive:
ng.module('smart-table')
.directive('stResetSearchOrder',
[
'stConfig', function(stConfig) {
return {
restrict: 'A',
require: '^stTable',
scope: {
row: '=stSelectRow',
defaultOrderBy: '<',
defaultReverseOrder: '#'
},
link: function(scope, element, attr, ctrl) {
element.bind('click',
function () {
var orderable = scope.defaultOrderBy;
ctrl.sortBy(orderable, scope.defaultReverseOrder != "true");
});
}
};
}
]);
Then add these attributes on your tr in your ng-repeat
st-reset-search-order default-order-by="the name of your property" default-reverse-order="false"
In my case I'm sorting by a function, and it works well.

How to set Max Length on ag-grid cells

Is there any way to impose a maxLength text allowed in an ag-grid cell, similar to the one on a normal input element?
<input maxlength="220"/>
No relative documentation was found. Also, no particular situations & more details are needed, in my opinion. Thank you!
agGrid's documentation really isn't clear on this, but it is possible.
agGrid MaxLength
In your column definitions, just add something like this:
this.columnDefs = [
{
field: 'Surname',
minWidth: 100,
editable: true,
cellEditor: 'agLargeTextCellEditor',
cellEditorParams: { maxLength: 200 }
}
Yes, you can control the full flow of input data, but you have to create your own cellEditor for that.
So - it shouldn't be hard to make a simple input validation.
and to achieve your requirements you have to take care of one function within the component:
// Gets called once when editing is finished (eg if enter is pressed).
// If you return true, then the result of the edit will be ignored.
isCancelAfterEnd?(): boolean;
isCancelAfterEnd() {
return !this.isValid(this.eInput.value);
}
isValid(value) {
return value.length <= this.maxLength;
}
Demo

Function.bind used in event binding will always re-render because it is not pure

Working on render performance on React, wonder what is the best way to tackle this performance issue. (The code is overly simplified for clarity)
var TodoList = React.createClass({
getInitialState: function () {
return { todos: Immutable.List(['Buy milk', 'Buy eggs']) };
},
onTodoChange: function (index, newValue) {
this.setState({
todos: this.state.todos.set(index, newValue)
});
},
render: function () {
return (
this.state.todos.map((todo, index) =>
<TodoItem
value={todo}
onChange={this.onTodoChange.bind(null, index)} />
)
);
}
});
Assume only one single todo item has been changed. First, TodoList.render() will be called and start re-render the list again. Since TodoItem.onChange is binding to a event handler thru bind, thus, TodoItem.onChange will have a new value every time TodoList.render() is called. That means, even though we applied React.addons.PureRenderMixin to TodoItem.mixins, not one but all TodoItem will be re-rendered even when their value has not been changed.
I believe there are multiple ways to solve this, I am looking for an elegant way to solve the issue.
When looping through UI components in React, you need to use the key property. This allows for like-for-like comparisons. You will probably have seen the following warning in the console.
Warning: Each child in an array or iterator should have a unique "key" prop.
It's tempting to use the index property as the key, and if the list is static this may be a good choice (if only to get rid of the warning). However if the list is dynamic, you need a better key. In this case, I'd opt for the value of the todo item itself.
render: function () {
return (
this.state.todos.map((todo, index) => (
<TodoItem
key={todo}
value={todo}
onChange={this.onTodoChange.bind(null, index)}
/>
))
);
}
Finally, I think your conjecture about the nature of the onChange property is off the mark. Yes it will be a different property each time it is rendered. But the property itself has no rendering effect, so it doesn't come into play in the virtual DOM comparison.
UPDATE
(This answer has been updated based on the conversation below.)
Whilst it's true that a change to a non-render based prop like onChange won't trigger a re-render, it will trigger a virtual DOM comparison. Depending on the size of your app, this may be expensive or it may be trivial.
Should it be necessary to avoid this comparison, you'll need to implement the component's shouldComponentUpdate method to ignore any changes to non-render based props. e.g.
shouldComponentUpdate(nextProps) {
const ignoreProps = [ 'onChange' ];
const keys = Object.keys(this.props)
.filter((k) => ignoreProps.indexOf(k) === -1);
const keysNext = Object.keys(nextProps)
.filter((k) => ignoreProps.indexOf(k) === -1);
return keysNext.length !== keys.length ||
keysNext.some((k) => nextProps[k] !== this.props[k]);
}
If using state, you'll also need to compare nextState to this.state.

JQGrid: Dependency Dropdown in dataEvents Change

I am developing a grid that contains a list of permits per module.
What I want is to validate every 2 events when a change is made in a combobox in a column. I'm using 1 and 0 for activating / deactivating
The first case: If I active "write", "modify", "delete" or "print" means that self-select "read"
The second case is the opposite: If you disable "Read" will turn off automatically "write", "modify", "delete" and "print"
Researching I found the option to use functions of input events:
{"name":"read",
"index":"read",
"width":48,
"resizable":false,
"editable":true,
"edittype":"select",
"editoptions":{
"value":"0:0;1:1",
"dataEvents":[{
"type":"change",
"fn":function(e){
if($(e.target).val() == '0')
{
// actions here...
}
}
}]
}
}
You can change the elements of the other columns ... by row?
EDIT
my solution:
$('tr.jqgrow select[name*="read"]').live("change",function()
{
if($(this).val() === '0') $(this).closest('tr.jqgrow').find('select.editable').not(this).find('option:first-child').attr("selected", "selected");
});
$('tr.jqgrow select[name!="read"]').live("change",function()
{
$(this).closest('tr.jqgrow').find('select[name*="read"]').find('option:last-child').attr("selected", "selected");
});
In the answer you will find an example how to implement dependent selects in jqGrid. By the way you can use the same idea with formatter: "checkbox". In the case the implementation will be much easier. It's important that you have to modify the <select> elements or chachboxes manually.
One more answer can you show another implementation option which you can use.

How to capture jqGrid column changing events?

In our application we are using a jqGrid which supports hiding and reordering of columns. When the columns are hidden or reordered we want to store the new settings into our database. But to do this we somehow need to capture the hiding or reordering event. Or possibly to capture when the colModel changes.
Is there any way to capture and handle these events?
Thanks.
You can use 'done' event of the columnChooser. Here is an example:
var grid = $("list");
grid.navButtonAdd(
'#pager',
{caption:"", buttonicon:"ui-icon-calculator", title:"Column choose",
onClickButton: function() {
grid.jqGrid('columnChooser',
{
"done": function(perm) {
if (perm) {
this.jqGrid("remapColumns", perm, true);
}
// here you can do some additional actions
}
});
}
});
UPDATED: If you define sortable option as
sortable: {
update: function (permutation) {
alert("sortable.update");
}
}
and not as sortable:true you will receive the notification about the new order of columns. See the source code of jqGrid for details. The array permutation with integers has the same meaning as in remapColumns functions (see my old answer for details).
You can capture column changes via the sortable parameter as mentinoed in Oleg's "update" above, or as discussed on jqGrid's message board.
However, please note that the array passed to your callback will be relative to the current column order. In other words, saving the array as is after moving multiple columns will not produce the desired results. See my answer on this other stackoverflow question.

Resources