Complex data structure in ViewModel layer of the MVVM - collections

I have large collection of MyFile objects which are linked in all kind of ways between each other like spaghetti.
In addition, from this collection I create smaller subcollections of some items that are equal by some criteria.
(for example all files with the extension .txt, all files that belong to certain directory etc...)
Basically I have complex structure of linked lists in my business logic. Now I want to create ViewModel for this
in order to prepare it for View and this is where I hit the wall. I just can't figure out how to prepare this mess
and still keep everything efficient and organized.
First problem is that wrapping each collection in collectionViewModel by enumerating item by item and creating itemViewModel
will create duplicate itemViewModel for each item (since one item can be contained in several collections)
Second problem is how to keep everything updated? If for example an item1 in business logic changes its reference from item2
to item3, then ViewModels should update them accordingly.
I am really tempted to break from the MVVM pattern here even though I dont want it, and put bussines + presentation logic
in one object/class since this spaghetti structure seems a bit too much for my level of understanding of MVVM.
Thanks

Maybe I'm barking up the wrong tree here, but here goes.
You could have a Model which acts as a repository for all your file objects, and that also exposes an ItemAdded and ItemRemoved event, plus a Query method. You can then have a common ViewModel type that represents your view on this model (a ViewModel), but specializes by composing a query. In this way you can have ViewModel+Query (e.g. all files with extension txt) instance for each view you need to represent. The ViewModel would be responsible for executing the query on your Model (by calling the query method) and then turning the results into an observable collection of file items (or what-have-you). You can update your ViewModel in response to Model changes by subscribing to ItemAdded and ItemRemoved events. If on an ItemRemoved event your ViewModel file items collection contains the item, then remove it. If on an ItemAdded event the item matches the query condition for that ViewModel instance, then add it to the collection.
This allows you to have a single Model for all your files, and then a ViewModel(+Query) instance for each type of view you wish to represent. The ItemAdded and ItemRemoved event allow you to update your ViewModel. As the items in the ViewModels are observable collections, your databound views will update themselves.

Related

How to delete records from a model referenced by a different model?

I have two models, where model A references items in model B. When I delete items from model B, react rerenders before I can update model A to also delete the referenced items.
Example:
I have a feed. The feed is made up of different types of items. Each type of item is stored in its respective model.
The feed order is stored as an array of item IDs, referencing the items in their different substores.
I have made a minimal failing example here. The issue occurs after clicking the button three times
https://codesandbox.io/s/deleting-referenced-items-kmw1e
The key line is
else console.error(">>>> feed order contains deleted item", id);
It's problematic that the feed order might contain deleted items because it could mean there is a programming error that resulted in bad references. In this case it's not a programming error, the second store just hasn't updated yet.
Is there a way I might be able to batch the createAndDeleteTodo, to not evaluate all listeners until the entire thunk and all subthunks have completed?
In the above example it's trivial enough to just have one master action which updates the feed order and the items but it would feel cumbersome if there was more than just one type of item as each item type lives in it's own respective model.
The same thunk action is triggering multiple Actions. And as per the definition of useStoreState:
The useStoreState will execute any time an update to your store's state occurs
So, in effect, when you do this inside the thunk:
actions.setTodos({ newTodos: [newTodo], todoToDelete });
actions.updateFeedOrder({ newTodos: [newTodo], todoToDelete });
there are two actions being dispatched and those would account for a separate store state change event listener. You will get multiple render calls for the multiple store updates.
You have two options:
Either club those actions into one as shown in the example: https://codesandbox.io/s/so-deleting-referenced-items-forked-x4d7v
OR check the useStoreState method for a case on handling the render only when both the store values are matching the count
It seems the problem is that you're dispatching to redux store 2 times. First when you create new items and then when you delete them.
I'd suggest to do it this way:
Create a deep copy of object/array you wanna work with.
Make both operations on that copy.
Dispatch that value to the store.
This way nothing will be rerendered until both operations are finished.

When passing an ObservableCollection using MVVM Light's messaging, is a copy passed or is a reference passed?

We're working on a new WPF app, using MVVM Light. We've got a customized ObservableCollection which starts as being bound to a datagrid. According to the project's specification we have to start on a form showing the datagrid and then when a user selects a row we show the user a detail view in another form. At this point we're getting the selected row and assigning that to an object which we assign to a MVVM Light message so that the detail view will be able to display the record's details.
Now that we're getting into this we've encountered a complication. The specs require that the detail view be able to navigate through the collection, even though at this point it doesn't have the collection. We had through we could accomplish this through an interface we defined that we called IRecordService, implementing it for each type of record we work with. However the problem is that the record has no idea if its the first record in the collection, the last one, etc. And that's necessary because of buttons on the detail form where people can navigate through the collection. We've been trying to do this with, for example CustomerRecordService, but so far that hasn't worked out. Perhaps it will if we keep at it.
But I've been wondering, what if instead of creating an object that has the selected record in it which gets passed into a message, we instead pass the whole collection and the key to the selected record into the message which then is caught by the detail viewmodel? My co-workers primary concern is how is the ObservableCollection passed, under these circumstances? Is a copy of the ObservableCollection passed or a reference to theObservableCollection that's in the listing viewmodel? I would think its a reference, but wanted to ask to make sure I'm right, or not.
It has to be just a reference. Otherwise messenger would have to know how to clone every single object. But you can easilly check it. After you get an object in your details viewmodel, change it. Add something, remove something and change some parameter of some objects in the collection. Then check if it has been changed in the main form with the grid.

POST Complex objects to controller

I am working on an enterprise scale project where I have a self referencing table called categories as below. Also in my current model I am using following table associations to fetch data. ( using EF6).
I have M-M mapping tables in DB for above M-M relationships.
In my controller GET action result returns the model which has Ilist(all parent categories). Then when the user is selecting items (in view) I am using Ajax to retrieve Ilist for next subset of categories (by passing to controller by Id - return Json result) and dynamically show next to the parent select list as below. ( and I have up to 4 subsets)
As the user is selecting categories, I am using Ajax call to load related categories and create selectlist or dropdownlist dynamically as below:
My question is Since all these data is loaded through Ajax, how can I bind them to my parent model. How can I capture user selected data into one parent model when user is posting the from to controller.
I know I can use Ajax and use Json to capture & transfer to contoller , But I need to use modelview , Can I use partiview to overcome this ?
Please advise the available options..
The key is making sure the name values match the object hierarchy accepted by the post operation. This is certainly doable; it's difficult because several AJAX operations build the UI, but one operation processes it, so you have to ensure the data expressions match the object being posted back.
You can use Html.NameFor() as a workaround to ensuring the naming paths work OK. Collections can make this difficult.

Group items by month in backbone collection view?

I have a simple collection of documents in my backbone app. This collection has a view that contains all the documents and they should be grouped by the month they were created in.
This view has to update in real-time - when I create a new document or modify an existing one, it should be added/moved to correct place in the view.
What would be the best way to achieve this?
You should create a comparator method for your collection (using groupBy, as suggested by the previous answer, or any other method that suits your needs). Then, you should have your collection trigger the render method everytime there's an "add", "remove" or "reset" event triggered so that the view is updated in "real-time".
for example, in your views initialize method, one way to do it would be:
collection.documents.on "add", #render
collection.documents.on "remove", #render
collection.documents.on "reset", #render
the purpose of the comparator method is precisely to keep collection elements sorted at all time.
Backbone collections has a bunch of methods, including groupBy. This could be used to group your collection.
The view have to listen to reset event of collection, so if the collection reset (mean, it's grouped and update), view have to update the DOM accordingly. The simplest solution, just full re-render of view. You can do some optimizations, if needed,

Linq to SQL Design question

Often I need to combine data from multiple tables and display the result in a GridView control.
I can write a Linq query inline in the Page_load event, return an anonymous type that combines all the fields that I need, and databind the result to the GridView control.
Problem: I use 'helper methods' as described by Scott Guthrie on his blog. Such a helper method cannot return an anonymous type. The query would have to be inline for this approach.
I can write a database view that returns the data that I need, and write a helper method with a query against this (new and known) type that it returns.
Problem: I will need a lot of views in my database schema, and I will introduce a lot of redundant aspects of my data. I also lose some of the advantage of using Linq - removing all business logic from the database.
I would like to take an approach that lets me keep the Linq queries in helper methods, yet allows me to access all the attributes that I need on the grid in their respective databinding expressions. Can this be done?
I asked the wrong question, as I frequently do. What prompted me to look into anonymous types was an apparent limitation of the GridView - my inability to use a databinding expression in an <asp:BoundField> (the DataField parameter only accepts column names of the table that the Linq query pulls in).
Turns out that in a TemplateField it is possible to use Eval and access members of the Linq data item, and Linq takes care of the query for me.
In other words, I can keep the query in my helper method, have it return a primary database table type (e.g. Account), and I bind the Accounts to the GridView.
In the databinding expressions I can access data members of the Account objects that reside in other tables, without having to explicitly pull them in in the query. Perfect.
I don't know if there is a viable way to achieve this using anonymous types. But I have a suggestion that will work in WinForms, but I am not sure about ASP.NET.
What you need is a type with properties where neither the number of properties, nor the types and names of the properties are known at compile time. One way to create such a thing is ICustomTypeDescriptor.
You have to create a type implementing this interface with an private backing store of objects backing the properties returned by the query for one row from the query. Then you implement GetProperties() to return one PropertyDescriptor per column and PropertyDescriptor.GetValue() and PropertyDescriptor.SetValue() to access the backing array.
By implementing PropertyDescriptor.Name you will get the correct column name; this will probably require another backing store storing the property names. And there is a lot more to implement, but in the end your new type will behave almost like a normal type - and now the if - if the control you are binding to knows about and uses ICustomTypeDescriptor.
UPDATE
I just found an bit of text stating that ASP.NET data binding knows and uses ICustomTypeDescriptor.
Scott's earlier post in the series talks about shaping the result set before inserting into a grid:
Part 3 - Querying our Database
Scroll down to "Shaping our Query Results".

Resources