Updating/Returning ViewModel Back To Server - javascript

Being fairly new to web dev and MVC4, I am encountering the same design issue repeatedly and was hoping someone could tell me what the right/supported/etc.. solution was in the MVC4 world:
Basically, I've drunk the view model koolaid and have view models for every view in my project, most of which are partial subviews dynamically updating on something of a single page application. All proceeds well generating/rendering the view, and then a user updates a number of values on the client side and it's time to update the server.
As an example, let's say it's a simple container view model:
public class Data {
public List<Prop> Props { get; set; }
}
public class Prop {
public string Id { get; set; }
public int Value { get; set; }
}
So let's just say the user is adding new props to the container. How do I get the modified object back to the server?
So far: If it's important that the server be in sync realtime, I can make a call with each addition/update on the server and then either keep things in sync on the client or simply have the server return the updated view. For simple scenarios like that, all is well.
But I find myself often in the case where I want the client to be able to manipulate the object (through the view/js/etc) and I don't really need to update on the server until they are done and submit. What I really want is to be able to pass the object down with the rendered view, interact with it via Javascript, and then pass the object back to the controller when all is done. How do I that? (Apologies it took a while to get to the point!)
Alternatives I've seen:
-- Quick & Dirty (Encode viewmodel properties to JavaScript in Razor): Which sure will put the object in javascript on the client, though it seems hack-ish to just be serializing the whole object into the client side html without any validation, etc. (Though I do realize eventually that's how the object is making it's way down, but it seems like you're bypassing the whole MVC4 object handling/parsing.)
-- Upshot.Js seemed promising with MS support at one time, but it seems that has died: Current status of Upshot.js
-- Breeze.js (http://www.breezejs.com/) seems to be an attempt to take over there, though my concern there is that it's fairly new and not much broad adoption yet.
Ultimately the thing that makes me feel like I'm missing what must be a somewhat obvious alternative is that all the pieces are clearly built into MVC4 already. When you use a form view for example, your fields are databound to the controls, and when the form is submitted, a parallel JSON object is created, sent to the controller, and then parsed into your POCO ViewModel object for you. That's basically the roundtrip I'm looking for (though retaining the full JSON object clientside). What's the "proper" way to handle that?

Knockout is actually exactly what I'm looking for (or another client side MV* framework), I simply didn't know how to use it.
Credit to #Mathletics and a couple of other SO solutions for point me in the right direction, especially: How to use knockout.js with ASP.NET MVC ViewModels?
The key is really to use the ko.mapping library, and Json encode or serialize the model on the server side. In particular, this script block is working well for me in strongly typed partial view:
<script type="text/javascript">
function viewModel() {
// Additional Knockout Here
};
var jsonModel = '#Html.Raw(JsonConvert.SerializeObject(this.Model))';
var mvcModel = ko.mapping.fromJSON(jsonModel);
var myViewModel = new viewModel();
var g = ko.mapping.fromJS(myViewModel, mvcModel);
ko.applyBindings(g);
</script>
And then you're all set up to use Knockout as per normal:
<div class="row">
<div class="span3">
<h3>Bound Value:</h3>
<span data-bind="text: PropertyName"></span>
</div>
</div>
(This requires knockout.js, knockout.mapping.js, and Newtonsoft.Json - though Json.Encode would work equally well.)
Passing the objects back is then easy. Update your object as normal, and when you pass it back to the controller, you already have you Json object ready to go and will be parsed as per normal to be passed back in as a POCO view model on the server side, completing the round trip.

From your description I can understand that you need to pass collection of objects to the controller and then update the collection. Model binding to collection will make things easier for you.
When binding to collection of single fields it becomes easier however when you want to bind to collection of objects then it becomes a little tricky. There are 2 options sequential binding and non- sequential binding.
Sequential binding:
Here is controller action where the form will be posted (HTTP POST action)
public ActionResult UpdateProps(ICollection<Prop> Props) {
return View(Props);
}
form method="post" action="/Home">
<input type="text" name="[0].Id" value="1001" />
<input type="text" name="[0].Value" value="Prop1" />
<input type="text" name="[1].Id" value="" />
<input type="text" name="[1].Value" value="NewProp" />
<input type="submit" />
</form>
When the form will be posted the values will go something like
[0].Id=1001&[0].Value=Prop1&[1].Id=&[1].Value=NewProp
You can see the indexing [0] and [1]. The model binder will rightly group the objects based on the indexing and you will get the expected collection. After the post your action will get 2 objects in ICollection Props. One with ID 1001 and another one which is newly added.
Now you will notice that if you add new prop dynamically then you need to index it properly using script. Or if that is hard for you then instead of indexing the elements you can post them in the above format. You need to parse the elements and post the form using script. The data should be arranged with index. The parsing gives you another advantage that you can post only the required elements i.e newly added or modified instead of posting them all thus saving the payload.
In non Sequential binding instead of series of number sequence the index is unique value like below:
[PropId1001].Id=1001&[PropId1001].Value=Prop1&[PropIdTemp233].Id=""&[PropIdTemp233].Value=NewProp

Related

Trying to load data into a new view

I have an issue attempting to create a "clone" of data on a page. The summary is that I have a create customer jobs screen and a separate manage customer jobs screen. These both work fine. I have a need to "clone" already existing customer job data to create a new similar set of jobs. So in my management screen, I have a clone button. I want this to head off to my "create" page with the data pre-populated with the original data, minus one or two pertinent identifying pieces of information, e.g. Job Name.
My issue is I cannot seem to get the post to redirect off to the Create page with my new data in the view model. I can't have a submit action as this saves the data; the data I'm cloning won't be persisted between the manage and create screens.
Here is where I'm at:
In my CSHTML:
<button type="button" name="btnClone" value="btnClone" id="btnClone" formaction="CloneJob" class="btn btn-primary" style="width: 150px;">Clone</button>
This fires off a click event to grab the current jobId which then fires off this function:
function CloneJob(jobId) {
$.post('CloneJob', { JobId: jobId }, function (data) {
window.document(data);
});
}
In my controller, I have this:
[HttpPost]
public ActionResult CloneJob(Guid jobId) {
// logic which wipes off the old data job from the view model
// eg names, ids, etc. but leaves the actual job details intact
return View("CreateJob", manageJobViewModel);
From this, I can see the HTML being returned in the IE/Chrome debug network section, but it won't go off to my CreateJob view populated with the data.
What am I missing?
First, if you're going to replace the entire tab/window contents, don't use AJAX. There's no point, and it's just something extra to maintain. Utilize a standard link, instead. Second, a POST should only be made when something is changing. This here should just be a simple GET request.
Create a link to the "create" URL and pass an id for the job that's being cloned. In the create action, then, you can look up that job from the database, based on that id, populate your view model with the data, and return it with the view.
If you want the appearance of a button, you can just style the link to look like a button, but you should use a standard old <a> tag, because that's the semantic meaning here.
I have managed to get further in that it now "works". My controller is unchanged, but my javascript is now:
$.when($.get('CloneJob', {jobId: jobId}))
.then(function (response)
{ $("body").html(response); });
I'm aware that this is still ajax, but in the face of an alternative that is more technically acceptable, this does work. Would be happy to take any advaces for a more technically neater approach as I understand that this is a bit of a misuse of ajax.
I have managed to get this cracked.
My cshtml now contains this code:
#Html.ActionLink("Clone Job", "CloneJob", null, new { #class = "btn btn-default", #style = "width: 150px" })
My controller just grabs the current viewmodel from the session and uses it to clone the job. Then, the controller now simply returns the view with the new model I set up within the method.
return View("CreateJob", clonedJobViewModel);
It's simple now when I look at it, but it's the getting there part.

Backbone.model.save(): POST(create) / PUT(update) logic doesn't match application logic - how to avoid PUT in certain situations?

I'm creating an Web-Application (Frontend and Backend, so both are under my control) using Backbone and Pyramid, being connected via a RESTful API.
During development I encountered a problem several times by now, where Backbone PUTs (=updates) a new model, while it actually should POST (=create) it.
Backbone decides whether to POST or UPDATE a model depending of the presence of an ID-field (if no ID present in the current model: -> POST/create | if so: PUT/update).
However I encountered several situations by now, where this behaviour doesn't match my application logic.
Let's say our main model (and its objects being persistently saved in a relational database in the backend) is called Foo, having fields like id, field_1, field_2.
Example #1: Creating a template or preview of Foo: Before creating (=POSTing) an object of Foo, I can create and show a preview to the user and/or save it as a template.
While doing so, the backend (in case of the preview: temporarily) adds the object to the database and returns the full model - including an ID in its HTTP response - back to Backbone.
Template- and Preview-objects of Foo are (temporarily) saved into the same table, as final objects (column type indicates its type (0 = final/live, 1 = preview, 2 = template)).
When now - after previewing / saving as template - trying to actually CREATE an object of Foo, the Backbone model already has the ID field set and actually PUTs and updates the template or not-anymore-existing preview, instead of POSTing and therewith creating a new Foo inside the database (as intended).
=> solution #1: calling POST /json/preview does not return the ID field, so Backbone doesn't get confused.
=> solution #2: overriding parse() of Foo in Backbone-model to kick out ID field from response
.=> kinda works
Example #2: Having a Periodic model, which refers to a Foo-template. Intention of a Periodic is to offer the user the possibility of semi-automatically creating a new Foo object based on a Foo-template every X months.
Now there is a call GET /json/periodics, which returns all Periodic-objects with its nested Foo-objects (Foo-templates), including their IDs, e.g. [{'interval': 12, template_id: 42, template: { 'id': 42, field_1: 'foo', field_2: 'bar', .. } , { .. } , .. ].
On the frontend the user now can periodically confirm (or skip) creating a new Foo-object, by issuing: periodics[X].template.save() which however again PUTs and therewith updates the Foo-model, instead of POSTing and creating a new one (as intended).
Here again (as in example 1), I could strip out the ID field of Foo - either in the backend or frontend.
However there are situations, where I need the id-field of templates, e.g. when actually editing them, so here I'd need two calls (GET /json/templates_WITHOUT_FOO-IDs and GET /json/templates_WITH_FOO-IDs). which also sounds far from right.
Question is: What's the right (and consistent) way of avoiding Backbone falsely assuming a model should be PUT instead of POSTed in certain situations / views?
Backbone's save and fetch methods just make calls to the Backbone.sync
method, which in turn is just a wrapper for an ajax call. you can pass
in ajax parameters using the save function without having to actually
extend it. basically ends up being something like this:
model.save({attributes you want to save}, {type:'POST', url: 'apiurl/model/:id/played'});
You would have to do this every time though so it is probably better practice to extend Backbone.sync for your model.
The Backbone website has a bit of information about what I'm talking about as far as the Backbone sync and save taking ajax options. There are also a few examples I've seen on extending sync but I can't seem to track them down at the moment.

Auto refresh label in MVC View

I have an auto refresh method (something like below) in my controller. In which I will update data in ViewBag, which I use in label of view. But I'm unable to refresh my label automatically. Please help me.
var waitHandle = new AutoResetEvent(false);
ThreadPool.RegisterWaitForSingleObject(waitHandle,(state, timeout) =>
{
// my viewbag
viewbag.time = DateTime.Now.TimeOfDay;
viewbag=date
}, null, TimeSpan.FromSeconds(5), false);
MVC doesn't work like WebForms (where reassigning a value populates to the UI). You'll need to write a separate action (or WebAPI method) and use AJAX/JavaScript to pull updates. (Alternatively you could use SignalR, but that may be overkill).
Also, if this is a recurring event, you may want to look into a library like Quartz.NET to perform the action. Then, post updates to a shared resource (looks like you're storing timestamp of last execution). From there, use setInterval/AJAX on the client to retrieve and display that value.

Conditional Binding with knockout.js/mapping plugin/koExternal template engine /json from ajax

I made a solution for a single page application, which will be used for a Large Screen display in a factory building, displaying status information and performance indicators for a set of machines. Via a settingsfile, which is derived via an ajax call the application knows which machines with which information should be displayed. So for every machine a template, and for each machine information a nested template is rendered(koExternaltemplateengine). Also the first ajax call contains the urls for the ajax calls for all machines. The respondes of these calls contain the machine-specific values. These calls are repeated periodically and the display values are refreshed. All bindings are made with knockout and the mapping plugin to avoid having a hardcoded viewmodel clientside. The frame and the machine containers(panes) are bound to the data from the first call, the nested templates data-fields are bound to data attributes contained in the machine specific ajax calls response.
Now my problem: If all ajax-calls for all machines deliver the required data for the display, everything works fine. BUT: For some machines, at sometimes(also on initial load), the calls are successfull (200) but contain just null (cause at this moment the data is not available).
Now I got the problem. After ko.applybindings(machinedata, machinediv) I got the ' Unable to parse bindings.' for the bound value fields and ko terminates binding. So the display is not rendered complete and no updates are triggered.
Now I will try something with the if/ifnot-bindings, but what happens if after a refresh the initial not bound values are present? (manually retrigger applybindings, additional to ko.mapping.fromJS??).
Did somebody have a similar problem? Any suggestions? As I mentioned I want to avoid hardcoded viewmodels to facilitate future extensions for more machine attributes.
You could store your machinedata in an observable:
Your view:
<div data-bind="if: machinedata()>
<div data-bind="with: machinedata()">
...
</div>
</div>
<div data-bind="if: machinedata()>
Data could not be loaded....
</div>
The view model:
var ViewModel = function () {
var self = this;
self.machinedata = ko.observable();
self.update = function () {
var data = someAjaxCall(); // returns the data or any falsy value
self.machinedata(data);
};
}
var viewModel = new ViewModel();
ko.applyBinding(viewModel); // data does not yet exist
viewModel.update();
As long as you only bound the loaded data and did not need any computeds you even would not need ko.mapping.

AngularJS and embedded framework html

I'm developing a user preferences page and I've decided to use AngularJS to control the state of the data.
What I would like to achieve is a page that shows the current user's settings and allows a user to edit them, cancel their changes, or save & submit changes if they so desire. I've binded my labels and my input controls to the model so that when a user modifies their email address, it is reflected across correctly.
I'm basing my work off this example #3 by the way: http://code.angularjs.org/1.0.0rc12/docs-1.0.0rc12/guide/forms
My question is related to the default values for the page. Since the model is defined as an object in Javascript. In the link above, default values can be set over here in $scope.master= {}; which works fine except that I'm using a framework to generate a static page and not retrieving an json object from my server. My pages are all written in embedded scala, so I access the values serverside. What is the best way to take the info retrieved serverside and turn into an object client side javascript can access?
You have a few choices. The way I would do it would be to push the initial state (through a WebSocket or something) to Angular as JSON, or have Angular pull it from the server through an HTTP call.
You could also perhaps render the page with ng-init tags to set the initial state, but this way seems weird to me.
<div ng-controller="FormController" ng-init="formData = {}">
<form>
<input ng-model="formData.input1" ng-init="formData.input1 = 'initialValue'>
</form>
</div>
I am using ASP.NET and Dapper-Dot-Net. I use the following techniques to get my data on to the page and then into Angular scope. It should be pretty transferable to your environment.
Define a DataTransferObject class in the page on the server side and a public string called DtoJson to hold the serialized version of your class instance.
private class DataTransferObject{
public User User;
public List<Project> Projects
}
public string DtoJson;
var dto=new DataTransferObject();
var userName="however you get a username";
dto.User=User.Get(userName); // I already have a User class
var sql="select * from projects where user_name=:UserName";
var p=new DynamicParameters();
p.Add(":UserId", dto.User.ID);
dto.Projects=Project.Query<Project>(sql, p); // returns a list of Project
// I use json.net to convert the Dto to json and expose it as a public variable
var DtoJson=Json.Convert(dto); // approximate syntax but you get the idea
On the client side I put a script tag at the head of the page and inject the DtoJson into it to get it into the global scope
<script>var dto=<%=DtoJson%>;</script>
At the bottom of my page I have a script src="pageName.js" which is my Angular controller file. Inside the controller I say
$scope.dto=dto;
console.log($scope.dto);
Now the json that was created on the server side is in my controller's scope and I can data bind to {{dto.User.FIRST_NAME}} and {{dto.Projects[0].ID}} ng-repeat="for p in dto.Projects" etc
I hope that that makes sense.
Peter

Categories

Resources