Is it possible to "inject" updates into a Knockout ViewModel? - c#

I'm currently looking for a solution to my issue: In our ASP.NET MVC application there are pages that are used for realtime data visualization of industrial devices. When the page gets loaded, a loading icon is shown while I fetch the viewmodel data with the current values for all the datapoints from a database. That works quite well, but it is static, by which I mean that the values don't change on the page after it finished loading. The web application itself uses a TCP listener that receives messages with values from the devices. These messages (which basically consist of a device id, a datapoint id and the value) don't arrive in fixed intervals but event-based, e.g. when a temperature value changes 0.5 K up or down.
On my page I have some graphical widgets like gauges and many other elements that correctly show the values from the initial data that gets loaded on the page load. They are bound to the Knockout viewmodel.
The problem is this: whenever a new value arrives on the server, I want to show it on the page without the need for a reload. I definitely don't want to re-transmit the whole viewmodel with some hundred datapoints on every message that arrives on the server (appr. 1 to 15 per second). In order to achieve that, I implemented the SignalR framework, which really works great. With that mechanism I now receive the new value in the client window (that means, I receive it in Javascript and now have a value object like described below).
What I need now is this: as every viewmodel gets built dynamically, they are all different. The object and properties tree is not the same for two devices, so each of them can have varying levels of subobjects. The only thing that is the same is the structure of the object that actually holds the value for each datapoint: it always consists of the aforementioned device id, the datapoint id and the value.
I need a way to update the double-type value inside the value object within the viewmodel whose device id and datapoint id match the newly arrived value message (that also consists of these two address-like ID's and the value).
I hope I got the idea across. Is there a way to do this? What would be the best practice for such a mechanism? I recently switched to Knockout-MVC (kMVC nuget package), but I'd also go back to "pure" Knockout.js and some additional scripting if that helps.
Thanks for your help and recommendations!

http://nthdegree.azurewebsites.net/mvvm-2/
Here is an article speaking to the Knockout mapping plugin
The general idea is that you should have your view model code which loads your model.
You would bind up your view model which will have a property you load from the server with an ajax call (see bottom of article).
You then just update the model with the result with
ko.mapping.fromJS(newData, mapping, modelToUpdate)
var mapping = {}; //define your mapping (see documentation or blog post)
function ViewModel(){
var self = this;
self.model = ko.mapping.fromJS({}, mapping);
self.hub = $.connection.myHub();
self.hub.client.updateModel = function(data){
ko.mapping.fromJS(data, mapping, self.model);
};
self.hub.start().done(function(){
//you could either make a call or have the "OnConnected" method trigger an 'updateModel'
});
}

ViewModel properties when linked to a DOM element allows editing which is actually updating the data in the KO observable array. Is it possible for you to change the "graphical widget"'s value (assuming that it is using some property to maintain height and width) using the IDs which you said are consistent?
I have not tested; the other option is to use KO foreach loop and update the related value.

Related

Should I bind my WinForm textboxes to local variables, to some data entity, or write helper functions to get the values

I am writing a c# winforms app that makes a couple of rest calls to a web service.
I have several textboxes with different behaviours. To take a specific example, one is called txtPrice. I have two buttons that increase or decrease the price across an (uneven) range of permissible values (btnIncrease and btnDecrease).
I need to parse the value in txtPrice to send it to the webservice. I have thought of three ways of doing this, and I would like some advice (or perhaps a pointer to look at a different way of doing it).
Have form-scope variables (private double price;) which are updated on txtPrice_Validated. I like this method as it separates the view from the model, but I dislike it because the data is duplicated. Also every time I update the price using the buttons I have to update it in two places.
Use member functions myForm.getPrice() and myForm.setPrice() which update txtPrice. Something about this seems ugly, storing data as strings, but it seems pragmatic.
Perhaps there is a kind of (non-database) entity I can bind to, which could encapsulate this logic?
More generally - is there a simple tutorial book which teaches a few basic exercises like this?

How to create my work object and keep it up to date?

Let's simply say I add, edit and delete some expenses in a database. Every class that works with these expenses should work with an object which represents a particular expense. Whenever a user enters some data into a form or data gets loaded, I want this expenses to be encapsulated in an custom object that should be kept up to date. So what is the best way to create my expense object?
I thought that whenever the value of a textbox, combobox and what not changes I let the eventhandler update the corresponding property of my expense object.
My other idea was, that whenever there is work to be done on a particular expense a method is called which updates the object. I don't like this idea because it's not intuitive.
Are there better ways to archive that goal of an always up to date object?
Edit:
Sorry, I wasn't clear enough. I already use Entity Framework. For example if I load some data from a database, I get an Entity Framework Object (EFO). I want the data from that object in my own object(MOO). So I convert it to MOO. Then, when I open a Form, I want that form to display the data of MOO. When a user creates an a new expense he creates MOO. If he edits an expense, he edits MOO. When I pass MOO to my DataAdapterClass for upload, then it gets converted back to an EFO. This is what I want.
So are you specifically asking about how to keep your "MOO" updated based on what is happening in the UI?
If you use WPF, this is basically what you'd use databinding for - you would bind your controls to MOO properties and let the bindings keep the two in sync.
What you are looking for is a mapper between your C# code and the database retrieval/inserting functionality that does the mapping to and from objects.
I recommend using Entity Framework for this as this is the industry standard provided by Microsoft:
https://msdn.microsoft.com/en-gb/data/ef.aspx

Which one is a generally better concept: store selected item or only its key (aka id)?

I have a quite big dilemma nowadays about general viewmodel design concepts. I mean general, like it's not exactly bound to a given language or environment: I had same dilemma when I wrote viewmodels for Winforms, WPF or KnockoutJS.
As a simplified use case, consider that I have a view where I have to select a country and a city from two select boxes. Both are represented in the database with a unique ID, a Name, and some other relevant information like - let's say - Population. Now imagine that I have to present a textual form of the currently selected data in for example the view's heading like "You've selected London, England". Now here is my two alternatives for creating a viewmodel, I will try to enumerate the pros/contras which I'm already thinking of below each version. The code is written in kind of pseudo way to be as generic as possible.
class RegionModel {
ID: number;
Name: string;
Population: number;
}
Version 1: Storing the selected object.
class MainView {
SelectedCountry: RegionModel;
SelectedCity: RegionModel;
SelectionInfo: string; // computed, should return the "You've selected ...." caption
Countries: List<RegionModel>; // datasource for country select
Cities: List<RegionModel> // datasource for city select
}
Pros:
Straightforward and easy to understand due to that the selected
item's type is the same as the selectable items' type.
Easy to compute such infos like "You've selected ..." because all the
members of the currently selected item are present directly.
Cons:
It holds more information than usually a consumer API needs. Usually
it needs only the ID.
If it's used in a client-side app, the whole selected object will be
returned to the server, consuming bandwidth.
If the consumer API needs only ID's (like in most cases), I have to
solve some kind of conversion before I pass it. In a web app probably
during serialization to JSON for example.
Version 2: Storing only the ID's of the selected items
class MainView {
SelectedCountryID: number;
SelectedCityID: number;
SelectionInfo: string; // computed, should return the "You've selected ...." caption
Countries: List<RegionModel>; // datasource for country select
Cities: List<RegionModel> // datasource for city select
}
Pros:
It's efficient in the way that it contains only the information which
is most likely needed by consumer APIs.
No additional conversion is needed, and efficiently can be passed
nearly "as is" to a server-side or other API.
Cons:
Not so straightforward and readable (in my opinion).
What about computing the info string? That's now much harder, I need
to grab the needed members from the selection source lists with a
search by the given ID, so it depends heavily on the consistency of
those lists (I mean the item must be present there).
I hope it won't be closed quickly as unconstructive. Any kind of advices, thoughts or experiences will be appreciated. Also, if the answer is "it depends", please try to give my some points where and when to use which.
UPDATE
I think my question was a bit unclear. I know about decoupling viewmodel from database entities, here I never mentioned database entities. I mentioned an "abstract consumer API". In a concrete scenario: if the API needs the selected items' Names, and my API needs only the IDs, which alternative should I choose, and where should do the conversion?
For example my server expects a data format like this (JSON):
{
"SelectedCountryID": 2,
"SelectedCityID": 5
}
and nothing else. How could I handle it in an elegant way? I would like to avoid repeating myself by doing a manual conversion.
Depending on how your datasources are implemented, it may make not difference: if you are retrieving the list of countries and cities, you can either store a reference to the selected value, to one of its fields or its index in the list.
Disregarding that, you should decouple your view model entities from your database entities and put into your view model ones only those fields required by the views. This way, your information traffic is minimized and your code is less affected by changes in the database.
EDIT following OP's update:
Talking about interacting with an API instead of a database, I think you can apply the same ideas, just replacing "database entities" by "service layer entities" (for instance, the JSON coming in/out your server). Take the returned data that into your view model objects, holding those attributes that you need. Obviously you also may need to store an id as you stated, when you'll need to refer to the same entity later on.
From a theoretical point of view, you should not include any other fields not consumed by the view, but you could do so depending on your requirements. For instance, in cases when you'll need to pass those fields back to the service layer and you don't want to query again by id to retrieve the service entity. However there are other alternatives to this (for example, some kind of cache), the exact balance depends on your requirements.
Base on MVVM pattern your viewModel should be an object with all properties which you need to display in view. ViewModel should be only used to be strictly binded to the view. Anyway your example it's not very good in my opinion. You shouldn't think about viewModel in case of storing something, please think more about presenting data.
Please remember that before you have data in database you have to insert it. So if you have some form with First Name and Last Name, user at first must fill this form and data must be insert into database, without it you don't have any ID.
To summarize in my opinion viewModel should have properties which you have to present to the end-user.

Knockout mapping and binding after null -> Object

I have a viewmodel which has some observables and observableArrays.
Depending on the page you are looking at it will fill up a certain observable or observable array. and then map it.
However from time to time there might be a subproperty which has currently a null value in it which i can create a new instance of through an api call (default values and so on server sided handled, seperation of concerns in mind, no Business Logic client sided!).
So when i do the $.get() request i placed it directly in the object and the data shows up in my viewmodel, however it is not mapped to the fields where they are bound to?
Binding works, but only one way (read). What do i miss? The api call (example), which places everything in the vm:
$.get('../../../../../api/addresses/?id=new&subjectid=' + subjectid, null, vm.currentSubject()['CurrentAddressInfo'])

Structural change in an Asp.Net MVC application backed by sprocs

Hello fellow developers.
First of all I apologize beforehand for the wall of text that follows, but after a day going crazy on this, I need to call for help.
I've stumbled across a problem I cannot seem to solve. I'll try to describe the scenario in the best possible way.
Task at hand: in an existing Asp.Net Mvc application, create a lookup table for an integer field, and use the textual value from the lookup in the editing view. When saving, we must first check if the lookup already has a corresponding text value for the same Root ID. If there is, use that. Otherwise, create it and then use it.
The structure:
The data model is a graph of objects where we have the root object, a collection of level A child objects, and every level A child object has a collection of level B child objects, so something like this:
Root (with fields)
Level A child (with fields) x n
Level B child (with fields) x n
The field we have to handle is on the LevelB objects.
There is a single Mvc view that handles the whole data. For collection objects, all fields are named like levelA1levelB1MyField, levelA1levelB2MyField, etc so every single field has unique name during the post. When the post happens, all values are read through a formCollection parameter which has average 120/130 keys. The keys are isolated by splitting them and looping on the numerical part of the names, values are read and parsed to the expected types and assigned to the object graph.
The datalayer part backing the object graph is all stored procedures, and all the mapping (both object to sproc and sproc to object) is hand written. There's a single stored procedure for the read part, which gets multiple datasets, and the method calling it reads the datasets and creates the object graph.
For the saving, there are multiple sprocs, mainly a "CreateRoot" and "UpdateRoot". When the code has to perform such tasks, the following happens:
For create scenario, "CreateRoot" is called, then the sprocs "CreateLevelA" and "CreateLevelB" are called in loop for each element in the graph;
For update scenario, "UpdateRoot" is called, which internally deletes all "LevelA" and "LevelB" items, then the code recreates them calling the aforementioned sprocs in loop.
Last useful piece of information is that the "business objects graph" is used directly as a viewmodel in the view, instead of being mapped to a plain "html friendly" viewmodel. This is maybe what is causing me the most trouble.
So now the textbox on the view handles an "integer" field. That field must now accept a string. The field on LevelB must remain an integer, only with a lookup table (with FK of course) and the text field from the lookup must be used.
The approaches I tried with no success:
My first thought was to change the datatype on the property MyField from integer to string on the object, then change the sprocs accordingly and handle the join at sproc level: I'd have a consistent object for my view, and the read/write sprocs could translate from string to integer and viceversa, but I can't do that because the join keys to retrieve the integer when writing are part of the Root item (as I stated in the first lines of this wall of text), which I don't know in the CreateLevelB sproc, and changing the whole chain of calls to pass those parameters would have a huge impact on the rest of the application, so no good.
My next try was to keep things "as they are" and call some "translation methods": when reading, pass the integer to the view, and there call the translation method to display the text value. When saving, use the posted text to retrieve the integer. The save part would work, I'd have all the parameters I need, but for the read part, I'd have to instantiate the "data access layer" and call its method at View level, and there's no need to explain why that is a very bad choice, so I ruled this out too.
Now I'm out of options (or ideas anyway). Any suggestion to solve this is very welcome, and also if something is not clear enough just point it out and I will edit my post with more accurate information.
Thanks.
This is not a real answer but you could rip out all sprocs and use the updating facilities of an OR mapper. This will resolve all the layering issues. You just update data how you see fit and submit at the end.
I guess this would also make the questions around "should I use an int or a string" go away.
Edit: After reading your comment I thought of the following: Do not implement alternative 1. You rather want to sacrifice code quality in the view than in the data storage model. The last one is more important and more centrally used.
I would not be too concerned with messing up the view by calling the DAL from it or the like. Changes in a view are localized and do not mess up the application's architecture. They just degrade the view.
Maybe you could create a view model in your controller and do the translations between DAL-model and view model? Or is that pattern not allowed?

Categories

Resources