I'm trying to preselect rows in my table but the table won't refresh unless there are changes to the actual data itself. Is there a method to reinit the table that doesn't involve changing the data?
It's also completely possible that my method for approaching this requirement is wrong and there may be a better way? I've created an example sandbox here:
https://codesandbox.io/s/mock-preselected-rows-data-t36nl?file=/src/App.js
In this you can see I have a mock response from my server for determining what rows should be selected. I'm then grabbing the data to compare to see if any of the items from the mock response exist in the data and if so push them to a new obj which is then fed into the intialState for selectedRowIds
Any guidance appreciated.
Seems your work is all working. The short answer to your question.
As long as you want the user see something, in a React way, it needs to be contained in a state, or state derivative. In your case, it's a cell data wrapped in row and in a table.
So you can't avoid selecting it without touching the data. Unless you don't want user see the change.
Although the checkbox doesn't seem to be part of the original data stream, when you develop on it, you have to make it part of the data. To be honest, it's easy you make it part of the data, because by the time you want to refresh the table, ex. selecting or de-selecting, or deleting a row, you want everything refreshed. Unfortunately it's very difficult to do local refresh with a table in React. It's possible, but very difficult, because most of the design is based on either prop or context.
You can also refactor your handleSelectedRows function.
// Find row ids and compare them with our 'preSelectedTheseItems' array.
const handleSelectedRows = () => {
const preIds = preSelectTheseItems.map(item => item.collectibleId)
return data?.filter((collectibleRow, index) => preIds.includes(collectibleRow.collectibleId));
};
Example : codesandbox
Related
I am using tabulator package 4.3.0 to work on a webpage. The table generated by the package is going to be the control element of a few other plots. In order to achieve this, I have been adding a dataFiltered function when defining the table variable. But instead of getting the order of the rows in my data object, I want to figure a way to get the index of the rows in the filtered table.
Currently, I searched the manual a little bit and have written the code analogue to this:
dataFiltered: function(filters,rows){
console.log(rows[0]._row.data)
console.log(rows[0].getPosition(true));
}
But the getPosition always returned -1, which refers to that the row is not found in the filtered table. I also generated a demo to show the real situ when running the function. with this link: https://jsfiddle.net/Binny92/3kbn8zet/53/.
I would really appreciate it if someone could help me explain a little bit of how could I get the real index of the row in the filtered data so that I could update the plot accordingly and why I am always getting -1 when running the code written in this way.
In addition, I wonder whether there is a way to retrieve the data also when the user is sorting the table. It's a pity that code using the following strategy is not working in the way I am expecting since it is not reacting to the sort action and will not show the information when loading the page for the first time.
$('#trialTable').on('change',function(x){console.log("Yes")})
Thank you for your help in advance.
The reason this is happening is because the dataFiltered callback is triggered after the rows are filtered but before they have been laid out on the table, so they wont necessarily be ready by the time you call the getPosition function on them.
You might do better to use the renderComplete callback, which will also handle the scenario when the table is sorted, which would change the row positions.
You could then use the getRows function passing in the value "active" as the first augment return only rows that have passed the filter:
renderComplete: function(){
var rows = table.getRows("active");
console.log(rows[0].getPosition(true));
}
On a side note i notice you are trying to access the _row property to access the row data. By convention underscore properties are considered private in JavaScript and should not be accessed as it can result in unstable system behaviour.
Tabulator has an extensive set of functions on the Row Component to allow you to access anything you need. In the case of accessing a rows data, there is the getData function
var data = row.getData();
as far as I understand there is only the callback dataFiltered, which is used for the whole table. It is triggered by all filters indifferently.
Is it possible to get a callback for a specific single header filter?
So that I can call a function as soon as a certain header filter becomes active?
I imagine it to be something like this:
{title:"Name", field:"name", headerFilter:true, headerdataFiltered:function()}
Is there possibly a workaround?
Thanks a lot!
(I would be especially grateful for non-jquery solutions)
Thanks also for this realy wonderful tool Tabulator.
Thanks for your kind words, it is always great to hear that Tabulator is appreciated.
The reason it is called when any filter is applied is because multiple filters can be applied at once, with Complex Filtering a complex and/or filter set can be applied so it would be hard to isolate down to specific columns in all cases.
The dataFiltered callback does get passed a list of all currently active filters so you can see if your affected column is in that:
var table = new Tabulator("#example-table", {
dataFiltering:function(filters){
//filters - array of filters currently applied
},
});
If you need to see if the column has just been filtered you can store a copy of the previous value of this object outside the callback and then compare the old and new values on the next call.
The other option would be to use a custom editor in the header filter then you could manually decide when the success function is called that initiates the filter and then reference an external function from there
I have a problem with handling state in a React application. For some background: The application mostly renders a big table with lots of data that is then editable. The data comes from a single source as a big list of objects (actually it’s a more complicated hierachy but let’s keep it simple for this purpose), and should be kept as it is. Users can then partially change the data in the big table, and ultimately save their changes.
Since the data comes from a single source, I’m thinking in React and store the data as the table state and pass everything necessary down to the individual components. So a row gets only the row data as a prop, and the cell gets only the cell data as a prop. For the update process at cell level, I then use an inverse data flow to call an update method on the table that updates the state for the updated cell:
change (rowIndex, cellIndex, value) {
this.state.data[rowIndex][cellIndex] = value;
this.forceUpdate();
}
This works pretty fine in theory. However, I have a lot data; the table easily contains about 1000 rows with multiple columns. This is not a problem directly: it takes a bit time for the browser to render the table, but once it’s there, it can work with it pretty well. Also, React doesn’t have a problem with that data amount either.
But the problem is that changing a single cell essentially triggers a rerender of the whole table. Even if the DOM is only changed for a single cell as a result, all the render methods are executed with most of them not doing anything (because the change only happened in a single cell).
My current solution is to implement shouldComponentUpdate for the row component and perform a deep check on all mutable values to avoid a rerender at row and cell level. But this feels very messy as it’s not only very verbose but also very dependent on the data structure.
So I’m not really sure how to tackle this better. I’m currently thinking about moving the state into the rows, and as such also the mutation functionality, and have the table component query the rows for changes on demand. Alternatively I could also move the whole data out of the table, and only work with identifiers the rows then use to query the data from a central store that provides the data and also offers mutation functions. This would be possible because the whole data is loaded once on page load, and then only mutated by the user.
I’m really unsure on how to handle this situation. I’m even thinking of dropping React for this, and rendering everything as static HTML with a custom JavaScript module on top that fetches the data on-demand from the actual input elements when a save is requested. Is there a way to solve this in a good way?
In case you want to play around with this situation, I have a running example on CodePen. As you type into one of the many input fields, you will notice a lag that comes from React calling all the render functions without really changing anything in the DOM.
You should take a look at PureRenderingMixin and shouldComponentUpdate documentation
I made some changes to your code so you don't modify state directly so shouldComponentUpdate can properly compare the props to determine if a rerender is required. The code here is a bit messy and I hacked it together really fast but hopefully it gives a good idea of how it could be implemented.
http://codepen.io/anon/pen/yYXbaL?editors=001
Table
change (rowIndex, cellIndex, value) {
this.state.data[rowIndex][cellIndex] = value;
var newData = this.state.data.map((row, idx) => {
if(idx != rowIndex){
return this.state.data[idx]
} else {
var newRow = this.state.data[idx].map((colVal, idx) =>{
return idx == cellIndex ? value : colVal
})
return newRow
}
});
Row
shouldComponentUpdate(nextProps){
return this.props.cells != nextProps.cells;
}
Working with React.js is really enjoyable.
I built a simple comment app based on the official tutorial.
You can add, edit, and delete comments. They are pulled via GET every 10 seconds.
At one point, the tutorial mentions optimistic updates: updating the UI before the server has responded in the case of a create, update or delete operation.
Since comments are part of a list, React suggests to assign a unique key to each comment.
I therefore use the database id of each comment as a key. That works fine for update and delete operations.
However, in the case of a create operation, I do not know the database id of a comment until it has been actually created server-side, and therefore I don't know what value to assign to the key.
At that point in time, the comment is added to the comment list but has no key/ID and therefore cannot be edited or deleted, until the list gets updated during the next API poll.
Can I work around that?
If you need the key to remain the same across updates, one option is to assign a temporary id to to an unused property. Then, use a function to retrieve the correct key for your list item models. As long as you account for the tempId property when updating the item, you can keep the key the same as long as the list remains in memory.
While you may not always care if the optimistic item gets removed and re-added, it can simplify your CSS when using on-enter or on-leave animations in your list. This also helps when you have stateful list item components.
Example:
let tempIds = 1; // 1 and up are truthy
// where ever you add the new item to your list
const newList = [...list, {...newItem, tempId: tempIds++}];
// get the right id
function getKey(instance) {
if (instance.tempId) {
return instance.tempId;
} else {
return instance.id;
}
}
// in your list render function
<List>
{list.map(model => (
<Item
key={getKey(model)}
//other props go here
/>
))}
</List
You need keys that are unique, consistent, and available. Your database IDs can't provide the third requirement, but you can--using local "client IDs". Obviously, you're responsible for guaranteeing their uniqueness and consistency.
You can add, edit, and delete comments. They are pulled via GET every 10 seconds.
We always POST to a resource which in returns yields the JSON response containing data we need, in your case ID. The delay is up to ~100ms which is fine.
If you set a temporary ID which is not equal to the one database is going to provide then React will re-render again once it receives the new data, just you will see two identical items as key is not the same.
I tried to find the answer a lot but no way. I have an Ext.data.ArrayStore store and want to get its data as string. I tried store.getRange(), store.getAt() but I couldn't figure out what these functions return. Is there any way to get ArrayStore data as string?
I am newbie to extjs, so if you have any example on this, I'd appreciate.
It really depends what you want to do with the data. For most UI widgets and that sort of thing, you'll want to just use the store directly. If you want to get a piece of data from the store for tweaking manually, that's a whole nother story.
store.getRange() will indeed return all of the records from the store, but they are returned as an Array of Record objects. Records contain an attribute called data which is an object containing any properties you defined in the record's config.
Example:
Ext.each(store.getRange(), function (item, idx, a) {
for (var i in item.data) {
console.log(item.data[i])
}
})
That should show you every item in every Record in store
EDIT: Changed my answer to not be totally wrong.