AntBlazor tree with checkboxes. How to check some tree items with code, when replacing the tree data - checkbox

I'm searching for a way how to set the check boxes in an AntBlazor Tree component by code for example on button press. Not at startup. In the end I would like to replace the tree data with a filtered set of tree data, and keep the check boxes checked that were checked before. In my attempts the check boxes are cleared when a new DataSource is set, and I found no way to re-set them again. I'm using AntBlazor 0.12.0.1. which is the latest version at this time. I used a simple test implementation : Checkable="true", #bind-CheckedKeys="CheckedKeys" , DefaultCheckedKeys="DefaultCheckedKeys", DataSource="People" (where people is List< Person > and Person has Children of List< Person > ) and no way to get to the checkbox item in the code using : #ref="PeopleTree". I also found no example code on how to do this. Any suggestions on how to approach this would be welcome.
(2 days later)
Finally I found a way to get to the checked property in code : (all objects in this piece of code where found and not null )
public void ReplaceTree()
{
var x = CheckedKeys.ToList();
CheckedKeys = null;
People = CreateTree(); // Resets the tree data to loaded data, all checkboxes are cleared
CheckedKeys = x.ToArray();
resetCheckedItems(CheckedKeys);
StateHasChanged();
}
private void resetCheckedItems(string[] checkedKeys)
{
foreach (string key in checkedKeys)
{
int id = key.ToInt();
var treeNode = PeopleTree.FindFirstOrDefaultNode((node) => node.DataItem.Id == id, true);
treeNode.SetChecked(true);
}
}
But even this does not seem to set the checks visually on the screen. All check boxes stay unchecked.
The code is working and can be used to set the checkboxes, as long as the data source is not replaced. The replaced tree data is the same and all keys which are Id's are also the same, and even the treeNode is found when I use "FindFirstOrDefaultNode", but the checkbox is not set now, when the data source has been replaced.
(8 days later) I have found a work around. It involves keeping the dataSource. I have found duplicate entries in the tree, what makes that the nodes need to be removed further like this :
var treeNode = PeopleTree.FindFirstOrDefaultNode((node) => node.DataItem.Id == id, true);
if (treeNode != null)
{
if (!removedTreeNodes.Contains(treeNode)) removedTreeNodes.Add(treeNode);
treeNode.RemoveNode();
}
When I then add the node again, it is unique and also the set checked will work if it will find the node again in the data.
The problem is that it will not find the node in the same piece of code that will add the data. It first requires the code to complete, and a "StateHasChanged" to have ran in order for the component to update it's node structure based on the data.
In order to set them checked in the same piece of code I created a function that runs asynchronously while the main code continues.
The function waits 1ms so that the main code has finished, and the StateHasChanged has updated the Tree component. And then sets the checkbox to the state that was saved. :
public async Task SetChecked(TreeNode<Person> treenode)
{
await Task.Run(() => { Task.Delay(1).Wait(); });
var treeNode = PeopleTree.FindFirstOrDefaultNode((node) => node.DataItem.Id == treenode.DataItem.Id, true);
if (treeNode != null)
{
treeNode.SetChecked(treenode.Checked);
}
}

Related

Showing the new row in react-table on the current page

I have been playing with ReactTable v7 for a while and have encountered the following problem: when the table is sorted and uses paginator sometimes adding (or editing) a row causes it to be outside the current page.
You can see the problem here:
https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/material-UI-kitchen-sink
Sort the table by First Name
Press add
Enter a record with First Name "zzzzz"
The record is added but is currently hidden which confuses users.
Is there a "standard" way to fix the issue? Am I missing something?
In v6 I have done a workaround for it:
React.useEffect(() => {
if (editedElementId && reactTable) {
const { data } = reactTable.props;
if (data && data.length > 0) {
const internal = reactTable.getResolvedState();
let position = -1;
internal.sortedData.forEach((row, i) => {
if (row._original.id === editedElementId) position = i;
});
if (position >= 0) {
const pageNumber = Math.floor(position / pageSize);
setPage(pageNumber);
} else {
alert.info("Element not visible");
}
}
}
}, [editedElementId]);
...
<ReactTable
ref={(r) => {setReactTable(r);}}
...
But maybe there is a bulit-in way to achieve it?
There is not currently a way to only sort the elements which are currently being displayed, no.
React Table v7's useSortBy hook sorts the entirety of the input data array, so sorting by First Name descending (A->Z) naturally places 'ZZZZZZ' at the end of the list, which will be hidden due to pagination. The v7 way of doing it would probably be similar to what you are doing already, using the exposed properties from the useTable hook instead of reactTable.resolvedState() and etc. Another way to do it would be to write your own custom sortBy method and only sort the items [0...n] where n is the number of currently displayed items.
As a side note, since the autoResetSortBy property is true by default in the example you linked, the step of sorting on First Name is irrelevant -- since the function which adds a new user to the list mutates the data array, the sorting method is reset. That function appends the new user to the end of the list, so it will always be on a new page, even if the "Rows per page" option is set to "All". Both issues can be fixed by setting autoResetSortBy to false, and changing the pageSize in addUserHandler.

Sort an array of objects based on another array of objects in angular 7

I know it's been asked million+1 times. But i've found no help in those questions/answers.
I have 2 arrays of 2 different objects one string property is used to uniquely identify them. This would be the key to sort about, but said object prop names are not equal (accessValue, modifiedOption). But their values are!
Object1: { ... accessValue, ... };
Object2: { ..., modifiedOption, ... };
array1:Object1[];
array2:Object2[];
I'd like to sort array1 based on the object indencies of array2.
So all of array1 items'd be in the same order as array2.
These two arrays are used to model a connected dropdown selection system, which can be added to are removed from. The Addition is screwing me over (lastly added item is appended to the first place and not the last) probably because of filter below?
What I use to add new dropdowns:
addFieldRow() {
this.fieldRows.push(0); // since desired selection is not known yet but need to populate the array that represents the 1st selection so a 2nd main selection dropdown will appear on screen
...
}
public onSelect() {
// if a selection is happened check the values of editOptions (contains data about all main selectable options)
this.fieldRows = this.editOptions.filter(
option => this.selectedOptions.some(el => el.modifiedOption === option.accessValue)
);
this.disableSelected(); // disable already selected items (not related to my issue)
this.optionSelected = true; // this is just for button disabled toggle
}
So either i need to figure out my addRow logic (if it has any flaws) or implement a sorting utility to make sure that the objects of fieldRows are in the same order as selectedOptions' -> since this models the selection directly.
I cannot really add a stackblitz since it's hard to model my current state.
Okay I am a complete idiot!
Since I know the current index (since i am looping through fieldRows).
All I had to do is replace this:
public onSelect() {
this.fieldRows = this.editOptions.filter(
option => this.selectedOptions.some(el => el.modifiedOption === option.accessValue)
);
With this:
public onSelect(index) {
this.fieldRows[index] = this.editOptions.find(option => this.selectedOptions[index].modifiedOption === option.accessValue);
this.disableSelected();
this.optionSelected = true;
}
Now it works correctly.

Adding views using for loop to scrollable view in appcelerator

Can anyone please tell me how to add an array of views using a for loop to a scrollable view in android. The answers that I surfed online are not giving me a clear idea and are way too confusing. Any suggestions?
You have (at least) two options. If you have a lot of work going on for each list item (e.g. showing a thumbnail for a picture) then you may want to use the second approach. Otherwise, you may just use this simple approach (where I show statistics for a number of species - layout controlled in a separate controller). stats is my list:
function showSpecies(stats) {
_.each(stats, function(record){
$.form.add(Alloy.createController('viewStatsRow', {record:record}).getView());
});
}
In this example I have more work going on for "building" every item. So to avoid locking the thread I use a list as a "queue" and just handles the first item - and then call the function with the remaining list until it is empty:
var work = [];
function showNextItem(work,first){
if(work && work.length > 0){
// Progressively show list....
if($.boastList && $.boastList.sections[0]){
var list = [];
list.push(buildOneItem(work.shift())); // Take first element
if(first){
$.boastList.sections[0].items = list; // Replace list
}else{
$.boastList.sections[0].appendItems(list); // append item
}
// Free queue to allow other actions
setTimeout(function(){
showNextItem(work); // Call recursively...
},30);
}
}else{
// All boasts shown...
}
}
function showBoastlist(){
work = [];
DataFactory.boasts.find({}, {$sort:{sortTime:-1}}, function(result){
result.forEach(function(record) {
work.push(record);
});
});
showNextItem(work,true);
}
The buildOneItem function just returns an item ready to be added to the view.
Not sure if this was what you asked for - but hope you can use it ;-)
Happy coding!
/John

How to keep "empty" selection on my combobox list in JavaFx?

In my application I hava combobox which is holding some values from databse ( some real time updated list ). This ComboBox list is updated every 1 minute.
List dosen't have null values. When I'm setting this list to ComboBox..
ComboBox box = new ComboBox(items);
.. there is one extra "empty" row, which is perfectly fine because non value is selected.
Right after I'm selecting some value my "empty" value disappears from the list.
My main question is How to keep this value on the list?
Why this is a problem..
Scenerio values is selected in database, first application start
List is loaded ( wiht selected empty value ).
Value is selected.
During first background refresh, empty values disappears, and combobox value is selected to n+1, next value is selected.
If I want to select empty values I have to all clearSelection from selection model / by some extra control.
While it is an old question, I spent quite bit of time trying to solve the same issue in my application and thought i might as well add my solution here.
One possible workaround is to create an extra list that contains null and the items you wish to be selectable.
ObservableList<String> selectableActivities = FXCollections.observableArrayList("a", "b", "c");
ObservableList<String> selectableActivitiesWithNull = FXCollections.observableArrayList();;
selectableActivitiesWithNull.add(null);
selectableActivitiesWithNull.addAll(selectableActivities);
In order to support updates of the original list you would need a ListChangeListener that updates the extra list according to changes in the original list.
selectableActivities.addListener((ListChangeListener<String>)(change -> {
while (change.next()) {
if (change.wasPermutated()) {
selectableActivitiesWithNull.sort((a, b) -> {
return Integer.compare(selectableActivities.indexOf(a), selectableActivities.indexOf(b));
});
} else if (change.wasRemoved()) {
selectableActivitiesWithNull.remove(change.getFrom()+1, change.getTo()+2);
} else if (change.wasAdded()) {
selectableActivitiesWithNull.addAll(change.getFrom()+1, selectableActivities.subList(change.getFrom(), change.getTo()));
} else if (change.wasUpdated()) {
for (int i = change.getFrom(); i < change.getTo(); ++i) {
selectableActivitiesWithNull.set(i+1, selectableActivities.get(i));
}
}
}
}));
And finally you use the extra list for the ComboBox items.
ComboBox<String> combobox = new ComboBox<String>();
combobox.setItems(selectableActivitiesWithNull);
Now you can modify the original list as usual and the ComboBox will update accordingly while also having an empty selection as the first item. And most importantly your original list will not be polluted by placeholder objects that could cause issues in other parts of the application.
This will also work with other objects, assuming that you add an apropriate StringConverter to the ComboBox. Note that the converter must also be able to handle null values if using the above approach.
StringConverter<Object> activityConverter = new StringConverter<Object>() {
#Override
public String toString(Object object) {
return object != null ? object.toString() : "";
}
#Override
public ActivityDataRow fromString(String string) {
return null;
}
};
combobox.setConverter(activityConverter);
While this approach is not exactly what you desired, I believe this is a close you can get without implementing a custom combobox.

ExtJS: Added grid rows wont de-highlight

When adding a rows to a grid, and then clicking on it, it gets selected (and highlighted). Then, clicking elsewhere but the new row remains highlighted (so now there are to highlighted rows).
Please, does anyone know what the problem could be? How to make it behave normally, i.e. clicking a row deselects (de-highlights) the other one?
After I reload the page (so the new row is not new anymore), everything works as expected.
Edit: Here's the code for adding rows:
var rec = new store.recordType({
test: 'test'
});
store.add(rec);
Edit 2: The problem seems to be listful: true. If false, it works! But I need it to be true so I'm looking at this further... It looks like as if the IDs went somehow wrong... If the ID would change (I first create the record and then the server returns proper ID, that would also confuse the row selector, no?)
(Note, correct as ExtJS 3.3.1)
First of all, this is my quick and dirty hack. Coincidentally I have my CheckboxSelectionModel extended in my system:-
Kore.ux.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.CheckboxSelectionModel, {
clearSelections : function(fast){
if(this.isLocked()){
return;
}
if(fast !== true){
var ds = this.grid.store,
s = this.selections;
s.each(function(r){
//Hack, ds.indexOfId(r.id) is not correct.
//Inherited problem from Store.reader.realize function
this.deselectRow(ds.indexOf(r));
//this.deselectRow(ds.indexOfId(r.id));
}, this);
s.clear();
}else{
this.selections.clear();
}
this.last = false;
}
});
And this is the place where the clearSelections fails. They try to deselect rows by using ds.indexOfId(r.id) and it will returns -1 because we do not have the index defined remapped.
And this is why we can't find the id:-
http://imageshack.us/photo/my-images/864/ssstore.gif/
Note that the first item in the image is not properly "remapped". This is because we have a problem in the "reMap" function in our Ext.data.Store, read as follow:-
// remap record ids in MixedCollection after records have been realized. #see Store#onCreateRecords, #see DataReader#realize
reMap : function(record) {
if (Ext.isArray(record)) {
for (var i = 0, len = record.length; i < len; i++) {
this.reMap(record[i]);
}
} else {
delete this.data.map[record._phid];
this.data.map[record.id] = record;
var index = this.data.keys.indexOf(record._phid);
this.data.keys.splice(index, 1, record.id);
delete record._phid;
}
}
Apparently, this method fails to get fired (or buggy). Traced further up, this method is called by Ext.data.Store.onCreateRecords
....
this.reader.realize(rs, data);
this.reMap(rs);
....
It does look fine on the first look, but when I trace rs and data, these data magically set to undefined after this.reader.realize function, and hence reMap could not map the phantom record back to the normal record.
I don't know what is wrong with this function, and I don't know how should I overwrite this function in my JsonReader. If any of you happen to be free, do help us trace up further for the culprit that causes this problem
Cheers
Lionel
Looks like to have multi select enabled for you grid. You can configure the selection model of the grid by using the Ext.grid.RowSelectionModel.
Set your selection model to single select by configuring the sm (selection model) in grid panel as show below:
sm: new Ext.grid.RowSelectionModel({singleSelect:true})
Update:
Try reloading the grid using the load method or loadData method of the grid's store. Are you updating the grid on the client side? then maybe you can use loadData method. If you are using to get data from remote.. you can use load method. I use load method to update my grid with new records (after some user actions like add,refresh etc). Or you could simply reload as follows:
grid.getStore().reload();

Resources