I am working with knockout and jquery with asplnet mvc. I apply bindings using knockout from the json object returned in a controller. This is a applied to my markup.
If a user clicks on a button on screen, I make a call to the controller action and retrieve the data from the server again.
At this point, do I need to rebind my new json object to knockout or how is it handled?
I am assuming that when a user clicks the button and makes a call to the controller action, you are doing this as an AJAX postback and not as a full page refresh. If this is correct, then no, you do not need to rebind your new json object. You just need to use your new json object to update your view model and knockout will take care of the rest. The way it works is as follows:
When your page initially loads, your page has a javascript object returned from your controller. If this object is turned into a corresponding object in which all of the properties are knockout observables and observableArrays, you can get persistent two-way databinding (i.e from the model to your page and from the page to your model) after you apply knockout bindings.
The easiest way to turn your javascript object into one in which all properties are knockout observables is to use the knockout mapping plugin. Once you have done this, you then bind this to your page using the knockout applybindings method.
So on the initial page load, this can be set up as follows:
//load your data
var data = ... some code to retrieve your data
//convert this into a viewmodel having knockout observables as properties
MyPage.ViewModel = ko.mapping.fromJS(data);
//bind this viewmodel to all knockout bindings
ko.applyBindings(MyPage.ViewModel);
Now when an AJAX call is made and new data is retrieved, when you update the ViewModel, knockout will automatically update the UI:
//AJAX call is made
var newdata = ... results from AJAX call
//Update viewmodel
MyPage.ViewModel.Property1(newdata.Property1);
MyPage.ViewModel.Property2(newdata.Property2);
...other properties...
...UI is automatically updated by knockout
Once the viewmodel is updated, the UI will automatically refresh thanks to knockout's two-way databinding. So you just need to update your viewmodel, and knockout will automatically apply its bindings and take care of updating the UI.
Related
Within a view I have a button which navigates to another view 'addWork'.
I want to pass an observable from the view model to the view model associated with the addWork view.
I already have addWork within the constructor for the view model, I'm just unsure what else to do.
The url action on the ko.applybinding is as follows, this work fine, I just need to amend so I'm passing the observable.
"#Url.Action("AddWork", "Work")")
The button is bound to this function:
self.AddWork = function () {
window.location.href = addWork;
}
An observable is purely a javascript concept and typically won't survive being sent to the server and back. You would be better off sending just the contents of the observable instead of the observable itself.
If you just need to send an ID then with asp.net MVC you can put it into your #Url.Action statement like Url.Action("AddWork", "Work", new { id = myID });
I´m starting a Proof-of-concept SPA using the new Web API and Knockout, so far I´ve managed to create the API controller, consume it with Knockout and map entities and arrays using Knockout mapping.
I´m now trying to create a simple CRUD, but I can´t get my head around as to how to implement the ViewModels.
So far I´ve come up with 2 options, listed below:
I can define a ViewModel on the server, that contains the entity´s attributes, plus an array of the same entity. When I enter the CRUD functionality, I call the server and retrieve that ViewModel, with the entity list and the attributes for creating a new entry.
I can define 2 ViewModels, one with the grid data, and another with the entity´s attributes. When I call the CRUD functionality, I get the grid data, and when I want to edit/create a new entry, I call the server and retrieve the ViewModel for that.
On both options I use one single view, which contains the grid definition, and the edit/create form format, which I display on a JQuery pop-up.
I can´t figure out which would be the best option, I´m starting to lean towards the 2nd one, but some guidance would be appreciated.
Thanks in advance!
Do you really need to call the server at the point that you launch the Create / Edit dialog? Could you not have, say, an ObservableArray of EntityVM (a Knockout view model) as the binding source of your grid, and when you either click Add New or click an existing item, the Create / Edit dialog is made visible (that could be done with binding too) with either an empty EntityVM as its data source or a populated EntityVM copied from the grid source's item? Then when you click Save, Ajax the entity as JSON to the server and return a JSON response representing the updated grid data? Or is that not the right understanding of your context?
I have a view that inherit the following:-
#model MvcApplication.Models.Application
But i need to know if it is possible to pass JSON objects to my view in the same way i am passing the model objects?
Since i have the following Controller:-
public ActionResult ListPackages()
{
using (var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
//code goes here ....
return Content(json, "application/json");
}
}
which returns JSON using a API call, and then i am displaying the JSON on the view using JavaScript as follow:
$.ajax({
url: $('#geturl').data('url'),
type: 'GET',
cache: false,
success: function (result) {
$.each(result.data, function (key, val) {
var str = val.packageName;
$('<li/>', { text: str })
.appendTo($('#products'));
});
}
The problem with displaying the JSON using JavaScript is that it will make too difficult for me to work easily with the JSON objects, such as creating links based on the returned JSON or creating table that contain the JSON. So my question is: Is it possible to pass a JSON object instead of a Model object from my controller to my view?
Server- vs client-side confusion
You're talking two things here:
Creating a view: controller passes model to the view on the server side and it doesn't make much sense to do so using JSON, because an in-memory object is being passed to view engine.
Consuming JSON data on the client: what you're talking about here is client-server Ajax communication where you request data from the client and get JSON returned from the server. This has arguably nothing to do with model data being passed to the view
Best solution using JSON
In order to easily consume JSON data (in your case it's an array of packages) on the client to generate resulting populated HTML is to use some sort of templating on the client side. jQuery used to have non-final templating plugin which is now a separate project. I've had great experience with it but there are other plugins as well. Use the one that you feel most comfortable with its syntax.
Where to put those templates?
If you know the structure of your JSON objects passed from the server at the point of creating your view, you can put templates in the view itself and they'll just wait untill being used on the client.
If you don't know the structure of your JSON objects then you'll have to pass templates either along JSON object or as a separate request.
The first approach is the usual one, the second one is rarely used and is much more dynamic.
Best solution not using JSON
If you don't like parsing JSON to HTML results (either manually or using templates), you can always make Ajax requests to your controller action, which would return a prepared HTML as a partial view instead of JSON result. This way, you could easily just put that HTML onto your page without any JSON data manipulation.
What do you gain here? Well suppose you have this functionality in your app:
You have a view that displays a paged list of packages.
When user first accesses the page first page of packages are being returned
Paging to next page is done via Ajax and the list is being replaced by returned data
If you'd create a partial view for your subsequent Ajax request, you can use the same partial view in your main view to display the first page of packages. This will ensure that you only have to change one partial view and display would change on inital page load as well as subsequent package paging.
If you used view + JSON + templating that means that you have to maintain two presentations of package list: the one being used in the view for the first page and the template that displays subsequent paging.
Which one then?
All things being equal it makes the second solution better. But the choice of course depends on your case (of things not being equal) and you should be able to determine which one is best in your scenario.
No, you can't. A view must be strongly typed to a model. So one solution would be to deserialize this JSON into a model object before passing it to the view:
public ActionResult ListPackages()
{
using (var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
//code goes here ....
var model = new JavaScriptSerializer().Deserialize<MyViewModel>(json);
return View(model);
}
}
where MyViewModel would of course reflect the JSON structure that you are working with.
I am starting a new project, and keen to make use of the KnockoutJS + Web Api which are new to me, I have a good understanding of the Web Api, but Knockout is tough to get my head around at the moment.
This is my initial thoughts of how I want my app to work:
I have a standard MVC controller such as LeadsController
LeadsController has an Action called ListLeads, this doesn't actually return any data though, but just returns a view with a template to display data from Knockout.
The ListLeads view calls my api controller LeadsApiController via ajax to get a list of leads to display
The leads data is then mapped to a KnockoutJs ViewModel (I don't want to replicate my view models from server side into JavaScript view models)
I want to use external JavaScript files as much as possible rather than bloating my HTML page full of JavaScript.
I have seen lots of examples but most of them return some initial data on the first page load, rather than via an ajax call.
So my question is, how would create my JavaScript viewModel for Knockout when retrieved from ajax, where the ajax url is created using Url.Content().
Also, what if I need additional computed values on this ViewModel, how would I extend the mapped view model from server side.
If I haven't explained myself well, please let me know what your not sure of and I'll try and update my question to be more explicit.
I think your design is a good idea. In fact, I am developing an application using exactly this design right now!
You don't have to embed the initial data in your page. Instead, when your page loads, create an empty view model, call ko.applyBindings, then start an AJAX call which will populate the view model when it completes:
$(function () {
var viewModel = {
leads: ko.observableArray([]) // empty array for now
};
ko.applyBindings(viewModel);
$.getJSON("/api/Leads", function (data) {
var newLeads = ko.mapping.fromJS(data)(); // convert to view model objects
viewModel.leads(newLeads); // replace the empty array with a populated one
});
});
You'll want to put a "Loading" message somewhere on your page until the AJAX call completes.
To generate the "/api/Leads" URL, use Url.RouteUrl:
<script>
var apiUrl = '#Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Leads" })';
</script>
(That's assuming your API route configured in Global.asax or App_Start\RouteConfig.cs is named "DefaultApi".)
The knockout mapping plugin is used above to convert the AJAX JSON result into a knockout view model. By default, the generated view model will have one observable property for each property in the JSON. To customise this, such as to add additional computed properties, use the knockout mapping plugin's "create" callback.
After getting this far in my application, I found I wanted more meta-data from the server-side view models available to the client-side code, such as which properties are required, and what validations are on each property. In the knockout mapping "create" callbacks, I wanted this information in order to automatically generate additional properties and computed observables in the view models. So, on the server side, I used some MVC framework classes and reflection to inspect the view models and generate some meta-data as JavaScript which gets embeded into the relevant views. On the client side, I have external JavaScript files which hook up the knockout mapping callbacks and generate view models according the meta-data provided in the page. My advice is to start out by writing the knockout view model customisations and other JavaScript by hand in each view, then as you refactor, move generic JavaScript functions out into external files. Each view should end up with only the minimal JavaScript that is specific to that view, at which point you can consider writing some C# to generate that JavaScript from your server-side view model annotations.
For the url issue add this in your _Layout.cshtml in a place where it is before the files that will use it:
<script>
window._appRootUrl = '#Url.Content("~/")';
</script>
Then you can use the window._appRootUrl to compose urls with string concatenation or with the help of a javascript library like URI.js.
As for the additional computed values, you may want to use a knockout computed observable. If that is not possible or you prefer to do it in .Net you should be able to create a property with a getter only, but this won't update when you update other properties on the client if it depends on them.
am new to MVC but have a Q about PartialViews. I have two PartialViews accessing Model Data from the ParentView (Model Data passed through via the ParentView's Controller). The first PartialView is used to update (add/delete values) Model Data to the database. The second PartialView generates a document based on the ParentsView Model Data. The problem is that if the data is changed in the database by the first PartialView then the ParentViews Model Data is now out of date and hence the Second PartialView (referencing the ParentsView Model Data) is now working with out of date data.
I realise the above should be re-engineered to to better suite, however is there a way to make the updated Model Data available at the ParentView level for the second PartialView to reference?
Usually in order to update something into the database an HTTP request is sent to the server and the controller action performs the update and renders a view meaning that the whole page is reloaded and the model data updated.
If you perform an AJAX request to update the database then you might need to update the second partial view as well so that changes are taken into account.