WebApi and Knockout patterns within MVC - asp.net-mvc

I am having some issues around the patterns needed to bring my application into the WebApi + Knockout world. The following scenario has thrown be a bit.
I have a very basic grid partial view (_grid.cshtml). It uses a GridViewModel, which only has a QueryID. The Grid partial passes the QueryID using Ajax (which is defined in the ko ViewModel) to a WebApi method which returns a collection of objects in JSON. I then use knockout to bind the columns of the grid based on the returned JSON, and then loads the rows in.
This all works fine, but I'm uncertain if this is the correct approach.
// When the query is changed update the grid data.
self.selectedQueryID.subscribe(function (newQuery) {
self.SelectQuery(newQuery.ID());
});
// Execute the query and set the results as the rows of the table
self.SelectQuery = function (queryID) {
$.ajax({
url: '/api/Query/Execute?ID=' + queryID,
type: 'POST',
contenttype: 'application/json',
success: function (result) {
self.gridData(result);
}
});
};
The complexity comes when I have another partial view which is a list of available Queries that a user can choose from. This partial view sits adjacent to the grid partial view. I want to be able to somehow send the clicked query across to the grid so it can then shoot another ajax request off to get the data of the new query. I know this is completely the wrong way to think about an MVC application, but I just don't know enough about WebApi.
#Html.Partial("~/Views/Shared/_Grid.cshtml", Model.GridViewModel)
#Html.Partial("~/Views/Shared/_Queries.cshtml", Model.User)
This is all in an effort to implement some sort of default query for the grid, then give the user the option to select other queries.
I have also started looking into Backbone and Knockback but don't know (enough about them or) if this is the right direction.

Related

How to do MVC form with very simple lookup?

We have a very simple MVC form with a dropdownlist which gets the user's selected city.
We have a list of cities and we know the average temperature (say) for each city.
Almost as a decoration (it's not a core function) for the form it would be nice to show the temperature for the selected city as the user changes it. Something like this:
This has to happen client side, I guess. Or is it AJAX?
At the moment our form is mostly out-of-the-box MVC4 EF auto built with scaffolding.
We populate the dropdownlist like so:
In the Controller:
ViewData("Cities") = GetCitySelectList()
and in the View:
#Html.DropDownListFor(Function(model) model.City, TryCast(ViewData("Cities"), SelectList))
... so pretty simple stuff.
There are plenty of tutorials for doing cascading dropdowns which are probably overkill for what we're trying to do here. Also if you google "javascript lookup mvc form" or similar a lot of the results tie you to using a framework (knockout? barebone?) which, again, I'm not familiar with and I don't know if I need for such a simple task.
In fact we're so geared to keeping it simple, and our city list is so short, it's almost worth doing it as a giant if then else in javascript (but, I know, yuk).
What's the simplest way to do this?
You can do it as follows:
Make an Ajax call on change on dropdown and then place the result in the corresponding div.
Html for result:
Temperature:<div id="temp"></div>
Ajax call :
$("#dropdownId").change(function () {
$.ajax({
type: "GET",
url: "Home/GetTemperature", // your url
contentType: "application/json; charset=utf-8",
data : {City : " + $(this).val() +"}
dataType: "json",
success: function (result) {
$("#temp").html(result); // place result in div
},
error: function () {
alert("Error.");
}
});
});
Your Action (C#):
[HttpGet]
public JsonResult GetTemperature(string City)
{
int temp = 30; // Get temp from your db..
return Json(temp,JsonRequestBehavior.AllowGet);
}
Your Action (VB):
<HttpGet>
Public Function GetTemperature(City As String) As JsonResult
Dim temp As integer = 30
Return Json(temp, JsonRequestBehavior.AllowGet)
End Function
You should use AJAX call to the server for the temperature. That's probably the "cleanest" way to achieve what you would like to have on your form.
Option 1: Using jQuery and make ajax call to fetch a temprature for the cityId passed and set the html of the temprature div or span container
Option 2: Eager Load on page as Javascript object
You could have eager load the cities and temprature into the JSON object and store it.
Here is the controller action code using JSON.Net for json serialization
public ActionResult myAction()
{
MyViewModel model=new MyViewModel();
List<City> cities=new List<City>();//fill the city id, temp
MyViewModel.CityJSON=JsonConvert.SerializeObject(List<City>);
return View(model)
}
View
#model MyViewModel
// other controls
#Html.HiddenFor(m=>m.CityJSON, new{#id='hdn-city-json'})
JavaScript
var cityList=JSON.parse($('#hdn-city-json')); // returns array of city obj
//do your stuff
If you are trying to avoid unneccesary requests to server, yes, you could eager load the whole collection. You can keep the data in a hidden <ul/> tag that you build on your View. Then, you can use jQuery to fire when the dropdown selection changes. But instead of making an AJAX call, you can lookup the temperature in the list instead and set the content of the target div to the return value of the lookup function.

Saving Cascading drop downs in MVC

I have cascading dropdowns in my MVC partial view. DD1 drives the values in DD2. When I select DD1 I want DD2 to be populated with, based on a DB table, the correct values based on DD1's value.
My thought was to make DD2 a partial view and nest in my form. Then, with ajax, I can tell the partial view to refresh and pass it the value of DD1.
The problem is, when I submit the whole view (with both DD1 and DD2 and a bucnh of other stuff, how do I get the value that is in DD2?
I'm trying to solve this problem using MVC, rather than triggering a javascript function on change of DD1 to make a JSON call to get the options and then using javascript to modify DD2 to the correct values.
How should I do this?
How big are your value sets for each drop down?
I was attempting to do this same thing a few years ago. DD1 was United States and Canada, and DD2 was the associated States and Provinces. If your data set is relatively small, you're better off just putting all the select list markup for both cases in the page, and then swapping it out with javascript (jQuery). You'll be saving yourself the request round trip, regardless of whether you go ajax or a full page refresh.
If the data set is large and it doesn't make sense to put all values in the markup, and you want to use MVC views instead of modifying the DOM with an ajax call, then just refresh the entire page. If you want to go ajax, then just modify the DOM with jQuery; you don't need a partial view to accomplish this.
You are going to have to use javascript unless you want to do an entire page postback. For this type of thing, javascript/ajax is the way to go. I personally had a hard time when I switched to MVC having to accept that all this business logic was happening outside of the MVC model. But in the end, it's whatever makes the website work best (user doesn't see your code and know how pretty it is).
Anyway, partials won't work either unless you post the whole page since without using javascript, the partial is rendered as part of that page/form.
I would just add a onchange event to the first dropdown that triggers a json call to a method in the same controller...something like
...jquery...
$("#mydropdown").change(function() {
$.post("/Controller/DropdownChangedJSON", { firstdropdownvalue: $("#mydropdown").val() }, function(data) {
$("#seconddropdown").empty();
// loop through "data" to populate dropdown
}); //post
}); //mydropdown_change()
and in your controller:
public JsonResult DropdownChangedJSON(string firstdropdownvalue) {
//get results
List<datamodel> myarray = //something
return new JsonResult { Data = new { success = true, rows = myarray } };
}
hope this helps

Can i pass JSON instead of Model from my controller to my Razor view

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.

ASP.NET MVC - Complicated view logic

I'm making the transition from webforms to MVC (I know, 3 years late) and I "get it" for the most part, but there's a few things I'd like advice and clarification on:
First off, what happens if you want to dynamically add inputs to a view? for example, in an old webform for generating invoices I had a button with a server-side click event handler that added an extra 5 invoice item rows. The stateful nature of webforms meant the server handled the POST event "safely" without altering the rest of the page.
In MVC I can't think how I'd do this without using client-side scripting (not a showstopper, but I would like to support clients that don't have scripting enabled).
The second problem relates to the invoices example. If my Model has a List, how should I be generating inputs for it?
I know data binding is a possible solution, but I dint like surrendering control.
Finally, back to the "stateful pages" concept - say I've got a Dashboard page that has a calendar on it (I wrote my own calendar Control class, the control itself is stateless, but can use the webform viewstate to store paging information) - how could a user page through the calendar months? Obviously POST is inappropriate, so it would have to be with GET with a querystring parameter - how can I do this in MVC? (don't say AJAX).
Thanks!
In MVC you design your actions to accommodate your needs. For example, if you wanted to be able to add 5 rows to an invoice NOT using client-side scripting, you'd probably have your GET action for the invoice generation take a nullable int parameter for the number of rows. Store the current number of rows in the view model for the page and generate a link on the page to your GET action that has the parameter value set to 5 more than the current value. The user clicks the link and the GET view generates the page with the requested number of rows.
Controller
[HttpGet]
public ActionResult Invoice( int? rows )
{
rows = rows ?? 5; // probably you'd pull the default from a configuration
...
viewModel.CurrentRows = rows;
return View( viewModel );
}
View
#Html.ActionLink( "Add 5 Lines", "invoice", new { rows = Model.CurrentRows + 5 }, new { #class = "add-rows" } )
You would probably also add some script to the page that intercepts the click handler and does the same thing via the script that your action would do so that in the general case the user doesn't have to do a round trip to the server.
<script type="text/javascript">
$(function() {
$('.add-rows').click( function() {
...add additional inputs to the invoice...
return false; // abort the request
});
});
</script>
Likewise for your calendar. The general idea is you put enough information in your view model to generate all the actions that you want to perform from your view. Construct the links or forms (yes you can have multiple forms!) in your view to do the action. Use parameters to communicate to the controller/action what needs to be done. In the rare case where you need to retain state between actions, say when performing a wizard that takes multiple actions, you can store the information in the session or use TempData (which uses the session).
For things like a calendar you'd need the current date and the current view type (month/day/year). From that you can construct an action that takes you to the next month/day/year. For a paged list you need the current page, the current sort column and direction, the number of items per page, and the number of pages. Using this information you can construct your paging links that call back to actions expecting those parameters which simply do the right thing for the parameters with which they are called.
Lastly, don't fear AJAX, embrace it. It's not always appropriate (you can't upload files with it, for example), but your users will appreciate an AJAX-enabled interface.
In MVC you can store application state in various ways. In your controller you have direct access to the Session object and you can also store state to the database.
your view can contain basic control flow logic, so, if your model has a list you can iterate over it in the view and, for example, render an input control for each item in the list. you could also set a variable in a model to be the maximum number of rows on the viewpage and then render a row in a table for the number specified by the model.
paging is basically the same thing. you can create a partial view (user control in the webform world) that shows page numbers as links, where each link calls an action that fetches the data for that page of results.
i'm not sure what your beef is with ajax or javascript

In MVC3 how do I have a dropdown that shows/hides fields based on the selected value?

I have a dropdown of countries, and an address form. Depending on the country selected, I want to hide/show certain fields. I'm quite new to MVC and MVC3, what is the best way to do this?
I have on the page a 'DropDownListFor' that populates correctly. When this changes, I imagine I need to ask the server which fields to show/hide. I could perhaps put some JQuery into a change event that calls a method, and it returns some json saying visible:true for each field, but I don't know if that's ideal or even how to implement it (possibly $.ajax or something).
Any ideas?
Edit: I should add the hard part of this is asking the server what fields to show for each country as there are many countries and the possibilities are all stored in the database. I am accustomed to webforms not MVC so I would ordinarily postback and have serverside logic, but this isn't an option with MVC afaik...
I have deleted my first answer as it was irrelevant.
With MVC3 you can send an AJAX request to any method.
In HomeController.cs:
public List<string> GetFieldsToShow(string id)
{
// if you routing is left to default, the parameter passed in will be called 'id'
// Do what you gotta do...
List<string> listOfFieldsToShowBasedOnCountry = GetList(id);
return listOfFieldsToShowBasedOnCountry;
}
And in the AJAX call, something like...
$.ajax({
type: 'POST',
url: '/Home/GetFieldsToShow/' + valueOfSelectedDropDownItem,
/*etc...*/
success: function(data){
$(data).each(function(){
$('#' + this).show();
}
}
});

Resources