MVC - Refresh View Model asynchronously via AJAX + Controller? - c#

I've been working on filtering a list of log entries, and I'm getting close to being complete.
I'm wondering if it's possible to update the current View's model asynchronously via $.ajax().
I currently have the $.ajax() working, however it returns the entire page's HTML as opposed to a partial view, or a model itself.
Is anybody aware of a way to simply update the model on the current view?

You'd have to return the model in json format and use js templating
functionality to replace the server rendered html in the dom. –
WestDiscGolf
The above is the solution, so I'll keep it simple and use a more conventional method.

This concept doesn't really make sense. The model doesn't really exist any more once the page is rendered, until it is reinstantiated on the following POST (assuming it is reinstantiated, i.e. your controller action accepts an argument of the same type). In the meantime, it is just manifested as form fields with name properties that correspond to what were the model's properties.
You can, however, dynamically create form fields that will (if possible) be mapped back to model properties again when the form is posted.
Create a field, whose name property matches the name of your model property:
<input type="hidden" id="hdnMyProperty" name="MyProperty" />
Then use jQuery to populate the field's value:
var property = /* Your returned value here */;
$('#hdnMyProperty').val(property);
There are a couple of common ways of achieving this:
Make an AJAX request to a controller action that returns a JsonResult and dynamically create the form field(s) in your Javascript (potentially - as has been mentioned - utilizing something like Knockout).
Create a controller action that returns a PartialViewResult and dynamically insert the returned markup in the appropriate place.
When you issue the next post request, the model binder will try to map this to an appropriate property in the newly-instantiated model.

You could simply use Knockout. No need to re-invent the wheel in this case.

Related

Dropdown list using Entity Framework in _layout page

Using Razor Pages. I want a dropdown list in the header which is created in _Layout.cshtml. This will be populated by a EF database query based on the login user ID. This list will be a global filter for any of the queries. It needs to respond to selection change. It will use cookies to keep track from session to session. Then each page that displays data will be able to access this value and filter its results. Each page could just read the cookie value or have some access to the value in the dropdown list.
Tried using partial and view component. Either way doesn't work because it always tries to pass the Model of the calling page (Index, Create, Report) since it is loaded by each and not exactly by the _Layout which doesn't have a model. So, now I can't use a model in the Partial or View Component to bind the drop down list.
I also couldn't get the changing of the dropdown list to fire the OnPostAsync event.
This seems like it isn't even possible based on all the attempts I made. I'm assuming it probably requires javascript to do all of the work but I'm not sure the correct approach to use.
You could create a base class for your controllers, which adds the required data to the ViewBag on every request. Note: The following code is based on ASP.NET MVC, not ASP.NET Core, but afaik the process is similar.
public class ApplicationControllerBase : ControllerBase
{
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
// use ViewResultBase, if you need logic for both full and partial views. Since you want to add info for the _Layout page, ViewResult is better.
if (filterContext.Result is ViewResult viewResult)
{
// Give the ViewBag item a unique name, that will not be used elsewhere, so you don't overwrite anything by accident.
// Use the data from ViewBag to populate your dropdown.
viewResult.ViewBag.DropDownItems = GetDropDownItems(); // implement GetDropDownItems accordingly, preferrably with caching!
}
base.OnResultExecuting(filterContext);
}
}
As for the second part of your question about calling the OnPostAsync when the DropDownList changes its value: Yes, you can use JS for this, unless you want to add an extra form around it with an extra submit button. The form may make sense in any case, but you can use the onchange handler to submit the form immediately, when the dropdown's value changes, instead of requiring manual submission of the form.

ASP.NET MVC razor view, post to a different model from the original model binding?

Well I have a group controller and view in the project, in which the model binding is GroupViewModel. But the group page is complex, and users will be able to make discussion topics. On this group view page, I have forms that allow users to post topics/replies. The model used for these forms can be TopicViewModel or ReplyViewModel, but the original model binding is only for GroupViewModel. It is declared at the beginning of the cshtml page:
#model MyProject.ViewModels.GroupBrowseViewModel
So I wonder, is it possible to have forms bind to different view model from the one declared at the top? If so, how to achieve this?
Model binding really has nothing to do with the model used in your razor view. At least not technically.
The first thing you have to understand is that there is no magic here. This is straight HTTP posted values, and if you don't understand how HTTP posting works, I suggest you read up on it. It's just a series of name/value pairs.
When you post, the routing framework looks at the selected action method, and the parameters that method takes, then it creates new instances of those parameters, and tries to match them up with similarly named values from the posted values.
So, in other words, there is no direct connection here between the model you use on the page, and the model used in the posted controller action. It's all based on naming convention. This naming convention is "helped" by the model you declare on the page, and the Html helpers create form fields with names that match up to model entries so that the model binder can figure these out more easily.
So, what this means is that in order to post to a different action, with a different model, all you need are fields in your form that have the names that the new model expects.
This can be done in a number of ways, from defining those fields manually, to using Partial view in which you pass an instance of the model you intend to post to as the model parameter.
You can use 2 partial views in the same view, and switch from the controller which one to show.
Or you can make a new ViewModel and this view model will contain references to both of your views, and in the view html based on your logic your can switch reading from any child ViewModel.

Can ViewBag be used to pass data from view to the controller or bind to model property

i know that ViewBag and ViewData can be used to pass data and objects from controllers to the corresponding view. Can they be used for vice versa, i.e. to pass data from view to the controller or to bind to model property. I have a file select in the view. Now can i pass that file using ViewBag to the controller or bind that file to my model property?
Thanks
The ViewBag or ViewData containers cannot be used to transfer data back to the controller. This is to due with the fact that the page is Stateless. Therefore once the View has been completely rendered (and all subsequent views) the ViewBag containers are disposed of.
To pass information back to the controller your options are using a HttpGet or HttpPost in the query string or form properties. This can be through traditional requests or via aJax requests.
You CAN however bind a value in your ViewBag \ ViewData to a model property. However that model property must be sent back to the controller using one of the methods above.
Hope this helps.
The life cycle of view bag only lasts for the duration of the initial request, so it won't be defined when you make additional requests.
See more here:
ViewBag/ViewData Lifecycle.
You can however do something like the following:
How can ViewBag data be saved after a form post?
Build route params for future requests etc

When do you use Html.Action over Html.Partial

I still don't get the primary purpose of Html.Action in asp.net mvc. I have been using Html.Partial every time I need to load up a partial view or wanted to split up some code in a view to clean it up.
Where does Html.Action fit into all of this (e.g. where would you want to use Html.Action and not use Html.Partial)?
Edit
The answers seem to be use Html.Action for dynamic data. I don't get this as you can use Partial Views for Dynamic data as well.
For instance if a user on my site edits a row. A ajax call is made to a method and I go grab that row from the db. I then return a parital view of a form that has all the data on it ready for editing. If an error occurs then I return a json result with the error method and my javascript consumes it and alerts the user.
If all is good then the rendered html is put into a jquery dialog and displayed to the user.
Is it because you can use the "ChildActionOnlyAttribute" that makes people use Action instead?
Ankur has the right idea but I find you can really simplify the concept down further.
For me it comes down to What versus How
If you know what you want to render but not how it's likely you'll use a partial to let it determine how to render the information.
For example, maybe your view model is for an invoice. Your invoice view model probably already has all the information you need about the invoice itself, including an enumerable of the line items on the invoice perhaps. A partial might be a good choice for the line items so that it's self contained. You already have the line items details (the what), but a partial will handle how it gets rendered (the how)
On the flip side, maybe your invoice view model has a customer ID on it but no actual customer details. Here you don't have the what, so you'd pass in the customer ID to an Action and it'll get what data it needs and pass it off to the view to render how it seems fit.
So in summary if you already have all the data you want to work with, just stick with a Partial, but if you are missing information that you need to obtain, Action would be better.
Where this get really fuzzy around the edges is when a Partial view includes the ability to retrieve it's own data via Ajax (or other technologies). In which case you might be able to get away with making that Customer details portion in my example, a Partial, and have it retrieve the data it needs Using Ajax after the client get's the response. But that's more up to you if that sort of thing even makes sense for your implementation.
Addendum:
It's worth noting that if you decide to try out ASP.NET MVC Core, ChildActions are no longer available. In which case your choices will be limited to partial views, ajax, or the newly introduced feature of Components. The last of which is similar to ChildActions, but slightly different in how they are implemented.
Perhaps an example will make this clearer.
Let's say you have a menu which appears on every page, so you put it in your layout. The menu will never change - there is just some basic navigation links, Home, About, Contact us etc, so you just use a normal partial view. This will work fine - as the content is static - you don't need to go to a database to get the data. You can just use #Html.Partial("Menu");.
Later you decide you need to change the menu so that it gets all the links from a database. You update your partial view to have a model that is a List<string> - one for each link.
Now, if you still want to just use a Partial View, every action would need to query the database to get the list of links, and every Model for every View would need to have a List<string> property for the links, so that it could pass this to the Menu Partial View. This would be a bad idea.
Instead, you would make a new Child Action GetMenuLinks() - this would query the database to get the links as a List<string>, and pass this to the Partial View. This puts the Child Action in charge of getting it's own data. This means you only need to have this query in one place, the 'About Us' action for example doesn't need to worry about getting the list of links for the menu.
Partial views
Use for sharing subsections of view markup between views. Partial views can
contain inline code, HTML helper methods, and references to other partial
views. Partial views do not invoke an action method, so they cannot be used
to perform business logic.
Child actions
Use for creating reusable UI controls or widgets that need to contain business
logic. When you use a child action, it invokes an action method, renders a
view, and injects the result into the response stream.
I use Html.Action() to load dynaimc content that I do not wish to contain in the view model (for instance, user information in a sidebar). It is very useful for keeping input and output view models identical.
Note that I always use Html.Action() in conjunction with applying the ChildActionOnlyAttribute to the controller method that I am calling so that the HTML fragment is not accessible via the URL.
Use Html.Partial when you require a subset of your view model to render the section in question--usually it is something that has to do with what you're working on. If could be a subsection of a form, a relevant content piece related to the main view etc. A partial receives its model information from the parent view.
Otherwise use Html.Action to render content that is independent of your main view, such as a navigation piece, an aside, or other distractions. Html.Action uses its own model provided by its controller, rather than provided by the parent view.
This question is answered (and elected for 149 times!) here:
Html.Partial vs Html.RenderPartial & Html.Action vs Html.RenderAction
Update
Sorry, meant to send you these postings rather
MVC Html.Partial or Html.Action\
#Html.Partial() Vs #Html.Action() - MVC Razor
Hope this helps.

ASP.NET MVC UpdateModel binding with a Hierarchy of Collections

I have the following Model design.
Template contains a list of Categories.
Categories contains a List of Items.
Thus there is 3 layer hierarchy. Because the UI design requires that all 3 levels be created on 1 page, I have had to write some of my own code to get this working. It looks as follows:
I can get it to submit back to the Create Action Method and the FormCollection looks as follows.
The problem I am having is that the UpdateModel works for the Template and Category level but the Items are not populated for the Categories. The Html looks as follows. As you can see, I have used the Category[guid_value].Items[guid_value] syntax for id. Shouldn't it figure out the binding automagically based on that?
Is there something I am missing or have to do in order to get the binding to work?
if you want your html to bind correctly to your model you have to pay some attention to html you render. The problem that i can see with your html is
<input type='hidden' value = 'Items.Index' value= 'some_guid' autocomplete = 'off'/>
with this html model binder will try to bind Items as children of Template object where it will not find any property named Items and these values will not be bound to the model. you have to change your hidden field to
<input type='hidden' name = 'Categories[category's guid].Items.Index' value = 'some guid' autocomplete = 'off'/>
and you have to just call updatemodel in your action method like
UpdateModel(yourobject);
i have not tested this code but i am more than certain that changing your html this way will get you sorted.
#Mlchael Grassman you don't have to use integer based id's to bind collection anymore. plz read steve's post for more information. Actually you don't need GUID either but a random number that is unique in page's context provided that you put an extra hidden field with name index and value set to above mentioned random number. I have blogged about Master detail form in asp.net mvc using (and modifying) BeginCollectionItem method written by Steve. in part one of this blog series i simply showed how you can dynamically add fields of your detail record on client side without ajax call. In second part i brought the editor template of detail record to jquery templating engine and rendering it on client side as it would appear if rendered through ajax call.
By default MVC uses the following convention for naming.
Category[0].Items[0]
Category[0].Items[1]
Category[1].Items[0]
Category[1].Items[1]
You can include your own Update by using the following TryUpdateModel. This woud probably need some sort of loop but should get you started.
TryUpdateModel(Category[Guid].Items[Guid], "Category[Guid].Items[Guid]", null, new string[] { "ExcludedPropery1", "ExcludedPropery2", "ExcludedProperyN", });

Categories