Has simple code in old version of ReactiveUI:
var allItems = new ObservableCollection<Model>(items);
var filteredItems = allItems.CreateDerivedCollection(
x => x,
Filter,
Comparer.Compare);
where Filter and Compare has simple signatures:
private bool Filter(Model item)
public int Compare(Model x, Model y)
sometimes i change items in other threads (big changes, without INPC) or change Filter\Compare strategies and just do filteredItems.Reset();
In DynamicData i try to:
ReadOnlyObservableCollection<Model> filteredItems;
var allItems = new ObservableCollection<Model>(items);
var cancellation = allItems
.ToObservableChangeSet()
.Filter(Filter)
.Sort(Comparer)
.ObserveOn(SynchronizationContext.Current)
.Bind(out filteredItems)
.DisposeMany()
.Subscribe();
but not found, how to Reset this^ or filteredItems.
Wish I were able to give you a direct answer, but I'm rather new to ReactiveUI. Managed to use DynamicData for something and thought you wouldn't mind this sharing.
If what I say later doesn't help you, here are the most relevant resources I could find:
https://floatingcube.com/blog/convert-reactive-list-to-dynamic-data/
https://github.com/reactiveui/DynamicData/wiki/Introduction-for-ReactiveUI-users
https://github.com/reactiveui/DynamicData/issues/182
https://habr.com/en/post/454074/
In my case I used SourceList or SourceCache as type for something similar to your allItems.
SourceCache<Model, string> allItems =
new SourceCache<Model, string>(m => m.Id);
// assuming each model has unique id; if it doesn't then use SourceList
I'd then have a BindingList<Model> as type for something simlar to your 'filteredItems'.
BindingList<Model> filteredItems = new BindingList<Model>();
The binding should be something like:
allItems
.Connect()
...
.ObserveOn(...)
.Bind(filteredItems)
.Subscribe();
To bulk-edit the list I'd call something like
allItems.Edit(
innerList => {
innerList.Clear();
// edit
// innerList.AddOrUpdate(...);
});
Cheers !
Related
Before java8 we write code like this:
List<ResultDTO> resources = Lists.newArrayList();
List<Product> productList = details.getProducts();
for (Product product : productList) {
ResultDTO result = new ResultDTO();
result.setName(product.getName);
result.setNumber(product.getNumber);
resources.add(resourceDetail);
}
And Java8 I write like this:
List<ResultDTO> resources = Lists.newArrayList();
details.getProducts.stream().forEach( product -> {
ResultDTO result = new ResultDTO();
result.setName(product.getName);
result.setNumber(product.getNumber);
resources.add(resourceDetail);
} );
I wonder if there are some tips when we traversing stream create ResultDTO objects set its properties and then to a list ?
Thanks in advance.
Creating a list and modifying it inside the stream operations is an anti-pattern. You should let the stream create the list for you:
List<ResultDTO> resources =
details.getProducts()
.stream()
.map(this::createResultDTO)
.collect(Collectors.toList());
Where the createResultDTO method would create a ResultDTO from a Product (you can of course inline its code inside map, but it's much less readable).
As JB mentioned, you would want to create list from the stream.
List<ResultDTO> resources = details.getProducts()
.stream()
.map(this::createResultDTO)
.collect(Collectors.toList());
And here would be the createResultDTO method.
public ResultDTO createResultDTO(Product p){
ResultDTO result = new ResultDTO();
result.setName(p.getName);
result.setNumber(p.getNumber);
return result;
}
You can put this method in the same class and use
.map(this::createResultDTO)
or in a different class called ProductResultDTO and use the following:
.map(ProductResultDTO::createResultDTO)
I'm a little puzzled over the possible cachedependencies in asp.net, and I'm not sure how to use them.
I would like to add items to the HttpRuntime.Cache in a way, that the elements should invalidate if I change other elements in the cache. The dependencies should be defined by the key.
I want a function like this:
public MyObject LoadFromCache(string itemDescriptor, IEnumerable<string> dependencies)
{
var ret = HttpRuntime.Cache[itemDescriptor] as MyObject;
if (ret == null)
{
ret = LoadFromDataBase(itemDescriptor);
//this is the part I'm not able to figure out. Adding more than one dependency items.
var dep = new CacheDependency();
dependencies.ForEach(o => dep.SomeHowAdd(o));
HttpRuntime.Cache.Add(
itemDescriptor,
ret,
dependencies,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.Web.Caching.Cache.NoSlidingExpiration,
Caching.CacheItemPriority.Normal,
null
);
}
return ret;
}
Help me out on this one.
I didn't know you could do this, but if you look at the CacheDependency constructor here, you can see that the second parameter is an array of cache keys, so that if any of those cached items change, the whole dependency will be changed and your dependent item will be invalidated as well.
So your code would be something like:
String[] cacheKeys = new string[]{"cacheKey1","cacheKey2"};
var dep = New CacheDependency("", cacheKeys);
HttpRuntime.Cache.Add(itemDescriptor, ret, dep ...);
I have an AdvancedDataGrid that relies on a HierarchicalCollectionView as it's dataProvider. What I'd like to do is sort the data when it is first loaded, but then disable the sort so that anything that is added after the initial load doesn't cause the grid to auto-sort again. I tried something like this:
this._myDataProvider = new HierarchicalCollectionView(
new HierarchicalData(this._model.rootTasks));
var mySort:Sort = new Sort();
mySort.fields = [new SortField("startDate")];
this._tasksDataProvider.sort = taskSorting;
this._tasksDataProvider.refresh();
this._tasksDataProvider.sort = null;
But setting the sort to null just leaves the data unsorted. I guess what I'm asking is: how can I sort the underlying hierarchical data since it seems setting the sort property will keep it dynamically sorting. Thanks for any help you can provide.
Personally, I would change the sort order when you're getting the data. Either it's done on the server side or when you parse the data (ie. in your model). You can do a one time sort using Array with sortOn.
you can
1. sort the original data with sort function,
2. clone content and put it to a new collection with no sort (be careful do and make a manual clone),
3. just use the new data collection.
I had the same kind of problem until I realized that the sorting with Sort object does not change the "physical" ordering of the items within the Collection, so when you remove the Sort, the next refresh reverts the view to the actual "physical" ordering.
Similarily as stated above, I solved by it by cloning the sub-collections into sorted order this way:
public static function buildPositionSort():Sort
{
var dataSortField:SortField = new SortField();
dataSortField.name = "position";
dataSortField.numeric = true;
dataSortField.descending = false;
var sort:Sort = new Sort();
sort.fields = [ dataSortField ];
return sort;
}
/**
* This method is used to create a clone of ArrayCollection, because sorting does not
* actually change the physical ordering of the items.
*/
public static function createSortedArrayCollectionCopy(source:ArrayCollection):ArrayCollection
{
var target:ArrayCollection = new ArrayCollection();
source.sort = buildPositionSort();
source.refresh();
for each (var item:Object in source)
{
if (item.children != null) item.children = createSortedArrayCollectionCopy(item.children);
target.addItem(item);
}
return target;
}
So this is driving me crazy. I've got an advanced data grid with a dataprovider that's an array collection w/ hierarchical data. Each object (including the children) has an id field. I'm trying to drag and drop data from within the ADG. When this happens, I need to grab the id off the drop target and change the dragged object's parentid field. Here's what I've got:
public function topAccountsGrid_dragDropHandler(event:DragEvent):void{
//In this function, you need to make the move, update the field in salesforce, and refresh the salesforce data...
if(checkActivateAccountManageMode.selected == true) {
var dragObj:Array = event.dragSource.dataForFormat("treeDataGridItems") as Array;
var newParentId:String = event.currentTarget['Id'];
dragObj[0].ParentId = newParentId;
} else {
return;
}
app.wrapper.save(dragObj[0],
new mx.rpc.Responder(
function():void {
refreshData();
},
function():void{_status = "apex error!";}
)
);
}
I can access the data I'm draggin (hence changing parentId) but not the currentTarget. I think the hierarchical data is part of the problem, but I can't find much in the documentation? Any thoughts?
event.currentTarget is not a node, it's the ADG itself. However, it's quite easy to get the information you want, since the ADG stores that data internally (as mx_internal).
I'm using the following code snippets (tested with Flex SDK 4.1) in a dragOver handler, but I guess it will work in a dragDrop handler too.
protected function myGrid_dragDropHandler(event:DragEvent):void
{
// Get the dragged items. This could either be an Array, a Vector or NULL.
var draggedItems:Object = getDraggedItems(event.dragSource);
if (!draggedItems)
return;
// That's our ADG where the event handler is registered.
var dropTarget:AdvancedDataGrid = AdvancedDataGrid(event.currentTarget);
// Get the internal information about the dropTarget from the ADG.
var dropData:Object = mx_internal::dropTarget._dropData;
// In case the dataProvider is hierarchical, get the internal hierarchicalData aka rootModel.
var hierarchicalData:IHierarchicalData = dropTarget.mx_internal::_rootModel;
var targetParent:Object = null;
// If it's a hierarchical data structure and the dropData could be retrieved
// then get the parent node to which the draggedItems are going to be added.
if (hierarchicalData && dropData)
targetParent = dropData.parent;
for each (var draggedItem:Object in draggedItems)
{
// do something with the draggedItem
}
}
protected function getDraggedItems(dragSource:DragSource):Object
{
if (dragSource.hasFormat("treeDataGridItems"))
return dragSource.dataForFormat("treeDataGridItems") as Array;
if (dragSource.hasFormat("items"))
return dragSource.dataForFormat("items") as Array;
if (dragSource.hasFormat("itemsByIndex"))
return dragSource.dataForFormat("itemsByIndex") as Vector.<Object>;
return null;
}
var dropData:Object = mx_internal::dropTarget._dropData;
should be
var dropData:Object = dropTarget.mx_internal::_dropData;
Try this.
I have 2 dynamic objects and I want to build one to contain all the properties:
var o1:Object = {prop1:val1,prop2:val2,prop3:val3};
var o2:Object = {prop3:val3a,prop4:val4};
and I need to obtain a third object that looks like that:
{prop1:val1, prop2:val2, prop3:val3a, prop4:val4};
Basically I need a way to iterate through the object properties and to add new properties to the third object. I have to mention I'm quite new to AS3/Flash/Flex.
First question, do you really mean to have prop3 in both objects? you will need to decide what to do in case of a collision like that, which object has precedence.
Secondly, check out the introspection apis: http://livedocs.adobe.com/flex/3/html/help.html?content=usingas_8.html
something like this should work:
public function mergeDynamicObjects ( objectA:Object, objectB:Object ) : Object
{
var objectC:Object = new Object();
var p:String;
for (p in objectA) {
objectC[p] = objectA[p];
}
for (p in objectB) {
objectC[p] = objectB[p];
}
return objectC;
}
If the property exists in A and B, B's will overwrite A's. Also note that if the values of a property is an object, it will pass a reference, not a copy of the value. You might need to clone the object in those cases, depending on your needs.
Note: I haven't actually tested the above, but it should be close. Let me know if it doesn't work.
Updated to fix the errors. Glad it works for you though.
You can dynamically access/set properties on objects with the index operator. The for loop will itterate over the property names, so if you put it all together, the following test passes:
[Test]
public function merge_objects():void {
var o1:Object = {prop1:"one", prop2:"two", prop3:"three"};
var o2:Object = {prop3:"threeA", prop4:"four"};
var o3:Object = new Object();
for (var prop in o1) o3[prop] = o1[prop];
for (var prop in o2) o3[prop] = o2[prop];
assertThat(o3.prop1, equalTo("one"));
assertThat(o3.prop2, equalTo("two"));
assertThat(o3.prop3, equalTo("threeA"));
assertThat(o3.prop4, equalTo("four"));
}
you can iterate over the object properties like:
var obj1:Object = new Object();
for(var str:String in obj2){
obj1[str] = "any value"; // insert the property from obj2 to obj1
}