Knockout JS, MVC 3 and View Model Server Side Data as JSON - asp.net-mvc-3

So I have a problem...I am separating concerns in my web app: HTML in razor pages and JS in js files. My problem lies in the fact that I want to use data from my controller passed down in the view model from the server as the options of a select list. The problem lies in the fact that I have separated my js from my HTML and I can't access Razor inside the js files.
I have a list of items coming down into my view model...
public List Stuffs { get; set; }
I json encode it server side and make sure to take care of circular refrences, so it looks like this
[{"id":1,"name":"blah"},{"id":2,"name":"blah2"},{"id":3,"name":"blah3"},{"id":4,"name":"blah4"}]
The problem is, I want to keep my separation of concerns, so how do I get that array into my js file so I can bind it to a select list using knockout? I definitely don't want to do another round trip to the server.

So lets say you have some view that looks like this:
<div> (Some stuff) </div>
<script type="text/javascript">
var initialData = //Your JSON
<script>
And then in your javascript (which you have linked below your view, so that it loads after it), you have something like this:
var data = initialData || {} //Or some other default value, in case initialData wasn't set
This will load in the initialData if it was set, or use nothing (or a default your define) if it wasn't. Loosely coupled.
Of course, more complex data will require you to standardize the strucure, but this is essentially one method that allows you to pull in data from the razor generated View without tightly coupling it to your javascript file.

Does your Javascript file contain a view model in the form of an instantiable function, e.g. something like:
function MasterVM(data)
or an object literal, e.g.
var masterVM = { ...
? I tend to go for instantiable view models (partly because they chain down through a hierarchy of view models using the mapping plugin - the top level one builds its children) and that being the case I think it's nice for the Razor view to instantiate the view model with the JSON rendered from the MVC view model, e.g.:
var masterVM = new MasterVM(#(Html.Raw(JsonConvert.SerializeObject(Model.ProductViewModel))));
Or even have the Knockout view model "owned" by a revealing module and initialise it like:
productModule.init(#(Html.Raw(JsonConvert.SerializeObject(Model.ProductViewModel))));
Then productModule is also responsible for other things like mediating between your view model(s) and the server, watching for dirty state, etc.
Also, doing another round trip to the server is not necessarily bad if you are sourcing a massive set of reusable options which you'd like the browser to cache. If you are, you might want to have an MVC controller which emits a big object literal containing all of your commonly used options, which you can then use as the "options" property across multiple selects. But if your options are specific to the particular view model, then I guess they have to be part of your view model.

Related

ASP.NET Core populate model only once for all views

I'm new with ASP.Net Core (3.0 in this case) and I´m trying to create a menu that is visible on all views of a WebApplication, is created dynamically and must be populated only once. Below i explain the steps and try outs i did to reach the goal needed (if required i can share the code I'm using).
This is what i did:
In a simple way, using the "_Layout.cshtml" page, i created a static HTML menu and made all other views simply inherit that layout. So far, so good;
Next challenge comes from the fact that the menu items are dynamically created after a User has logged-in, which i managed to overcome by setting a ModelView inside a controller (HomeController.cs with Index action in this case), and then delivering it to the view. For this case works OK, because the default page is ~\Home\Index\, problem is when i change to a different view with a different controller, the menu has to be rendered again, and so i have to replicate the code (a problem dealt create a BaseController and BaseModel based on this post along side the OnActionExecuted to host the menu generating code)
Now, the biggest problem is the fact that i can only populate the menu once, after the user logs-in. Each time there is a redirect between different controllers/views (post-back of same controller/view works fine), the model is null inside the OnActionExecuted, I tried using ViewData, ViewBag, TemData, but all are null.
So, my question is, how to keep that specific data alive and shared, basically across all the views, and only gets populated once (after each user login) between redirects from different views?
I have been reading around and found several solutions besides the one i did, but i did not found any that could keep data alive throughout the user session the way I need:
ViewBag, ViewData and TempData
Can the shared layout view have a controller in ASP.NET MVC?
Pass data to layout that are common to all pages
To sum up, my flow at this moment, is like this:
User Logged-in
Redirect to default: ~\Home\Index
MenuModelView.cs for the menu gets built and HomeController.cs returns to Index.cshtml with the model attached to it.
Index.cshtml receives the populated ModelView and it uses _Layout.cshtml
The _Layout.cshtml builds the HTML tags for the menu based on the MenuModelView.cs data
User navigates to a different view and steps 3 to 5 are repeated from a specific controller/view
If you want to create a control that can be accessible in all pages without changing every controller, I strongly suggest creating a view component. For a view component has no relationship with your controller, but can access dependencies like database and full HTTP context.
For example, you want to build a custom nav menu, you can just create a view component named NavHeader
using Microsoft.AspNetCore.Mvc;
namespace YourProject.Views.Shared.Components.NavHeader
{
public class NavHeader : ViewComponent
{
public NavHeader(YourDbContext context)
{
// you can access your dependencies, like database.
}
public IViewComponentResult Invoke()
{
// your own logic. You can access HTTPContext here.
var model = new YourOwnModel();
return View(model);
}
}
}
And just call it in any view or layout.
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
#*Render your component like this*#
<vc:nav-header></vc:nav-header>
</body>
For more details about view component, please reference:
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-3.1
https://anduin.aiursoft.com/post/2020/1/4/share-view-component-between-different-aspnet-core-web-project

using ajax or not when displaying new records in asp.net mvc

I am a beginner in MVC. In my application, I list categories (as a button) on the left hand-side (inside a div). Just right to this div, there is another div which displays the items associated with the clicked category.
When a category button is clicked, I am planning to call the action of the controller that will retrieve the Items from the database and add these Items to the ViewModel (e.g., model.Items = db.Items...), and then call the View with the updated Model and display the Items.
However, I am curios if it is better to make an Ajax call here and use a partial view for displaying the Items of the clicked category.
If feel like between these two approaches in my scenario only difference will be the page-refresh, they should work the same in terms of speed since both of them require the same database call.
Can anyone share good practices in MVC for such scenarios?
Yes AJAX is faster and good way to update detail in same page without refresh.
For that you have to create JsonResult method in controller. It will give you result in Json.
Try JQuery Template plugin for repeated code.
<script id="trTemplate" type="text/x-jquery-tmpl">
<tr>
{{each $data}}
<td>${Col}</td>
{{/each}}
</tr>
</script>
<table id="containerTable">
</table>
AJAX Call
$.ajax({
url: 'Your JsonResult Method URL',
type: "GET",
data: data,
beforeSend: function () {
},
success: function (data) {
// It will pass data to template and template will bind parsing json
$('#trTemplate').tmpl(data).appendTo('#containerTable');
//Business logic
},
complete: function () {
// Your Code
}
});
Your JsonResult Method
[HttpPost]
public JsonResult GetData(ViewModel model)
{
// Your Code here
}
using ajax or not when displaying new records in asp.net mvc
Using jquery would be the best approach for this scenario. As you don't have to load the layout page which will have to render the scripts and stuff all over again. Stick on to Ajax calls in MVC as much as possible, The technologies are being improved and a lot of single page applications are out there, And if we still use a page load for every new request then there is no point.
Coming to comparison between passing back Partial View And Json Data. Which is better to use in the application design?
Both partial Views and Json data hold the same weight depending on the scenario.
When to use partial view: Lets say you have a Model and you have to build the view HTML by lot of if checks and loops and possibly some c# code ( in rare scenarios), etc, in such scenario using Partial view would be the better choice, Because if we try to build the same thing in Jquery using json data the complexity of the code required would be high compared to what can be done in Partial views, But still achieving it is possible but wont be that easy and we might make errors during development.
When to use Json Data: If the requirement is like updating a grid, generating dynamic drop down or dealing with some Jquery plugins in the page I think Json data would be better, as many plugins play with json data as the core requirement.
A Small Example Of Deciding Between Partial View And Json Data - interested folks and read through
Lets take a scenario where we have to display a grid of data. This is our initial requirement. So we can happily build our viewModel with data and pass it to our partial view and render the table using for loops. All set, Now the requirement changes and we are asked to build sorting, filtering and paging stuff in our table. So at present we look for a plugin that can be easily integrated with current code and yes the easy one to use at this scenario is Datatables. Ok, we wrote a small Jquery to apply the plugin to the table and all set we have the fancy stuff ready.
Now here is the tricky part, we are asked to add functionalities like add, edit, delete record from the table. Yes its possible but is little tricky to get it done in the best possible way with the current code which we have. What we tend to do is, when ever there is a change in the table we plan to recall the partial view. Which works fine but still asking to ourselves just to delete one record from the table is it good to reload the partial view again?? Definitely NOT,
What can we do? When ever there is any add, edit, delete operation we hit the controller to update the database and we can make the controller return a JSON data and just pass this Json data to the plugin API and refresh the table, This will be more neat and faster. So here you see JSON data would be the better choice. Also some might even want to make it more cleaner by just playing with that one record of data and writing up some jquery code to manipulate the table, which is absolutely fine, But it requires us to pass the Json data itself back from controller.
So having this done, we can go back and refactor our code to make partial view to use json data for the grid initially too or leave it as it is saying the initial load will be a partial view, but following operations would be a json result, which is fine but I feel let all the data related stuff come from one point.
So that explains how a simple module can change from being a partial view to then use Json data. There are scenarios where the story is the other way around, You have to pick the right one for the right work.

mvc3 - set focus to control from Controller

Can I set focus to a control from a Controller when calling a View?
(I understand the typical best practice is to use jQuery to set focus to a control when the page is loaded.)
The controller's job (one of them) is to set up a view model which gives the view enough information to render correctly. In other words, the controller and the view should only be loosely coupled.
Here's one way to do it. This is somewhat decoupled though it could be done more elegantly. You still need JavaScript to perform the client-side scripting, but the script is generated based on a value in the view model.
Controller
public ActionResult Foo(){
var model = new MyViewModel();
model.SelectedItem = "FirstName";
return View( model );
}
View
#model MyViewModel
#Html.TextBoxFor( o => o.FirstName )
#if( Model.SelectedItem != default( string ) ){
<script>$("##(Model.SelectedItem)").focus();</script>
}
One thing you have to learn about web development is that you can have all the technologies in the world on the server, but in the end, those technologies have to generate standard (or what passes for it) Html, CSS, and JavaScript.
That means there is no special magic that can be done on the server to automatically do things on the client. Some frameworks can automatically generate code to do this for you, but it still must be done as standard html/css/js in the end.
MVC only renders standard, plain HTML. Webforms will do a lot of things for you, but in the end Webforms has to generate standard HTML as well. It does this by auto generating javacript that gets included in the page that, on load sets the focus.
MVC doesn't do any of those things for you, so you would basically have to do the same thing, but you would have to write it. It's relatively simple using some simple jquery.

How to get view model directly in layout?

I'm working on a practice where I minify most of my javascript in static files, and then each view has something like this:
#section Script
{
#Html.Action("MinifyJavaScript", "Resource", new { viewPath = "~/Views/User/Register.Js.cshtml", model = Model })
}
Which in turn renders the tiny, non-static, pieces of javascript code, like the one below:
#model UserRegisterModel
<script>
(function ($, b) {
$(function () {
b.views.user.register('#Url.Action("ValidateInput", "User")');
});
})(jQuery, bruttijjimo);
</script>
This allows me to cache javascript in views more heavily, since only the parts that vary with the model can change (and are generally treated as partial views)
Now I'd like to further upgrade this practice by removing the need of a layout section, and by convention, render the javascript partial view (which is passed the same model as the view) right after the view. I already created the method to compress the javascript in the view. And there's also the convention that javascript for a view must be in a .js.cshtml file, and share the model with the view.
What I'd need is to grab the model for the view from the layout, and the name of the view, too, and render it there.
This would only work or be needed for the actual view, since partials use a script manager if they need to emit javascript.
So: how can I grab the model for the view from the layout? The rest I can figure out, but this one is eluding me..
So: how can I grab the model for the view from the layout?
#Model should give you what you are looking for.
This being said I didn't quite understand what you are doing but the fact that you have javascript inside your view instead of having it in a separate javascript file and leaving the caching stuff to the browser and reinventing some wheels kind of feels wrong.

Ember.js: How can I decouple my views from my controllers?

I'm trying to restructure tightly coupled portions of an ember.js app, particularly views and templates to controllers.
All the examples I've seen bind views directly to controllers in the view's class definition, or by pass the (global) controller path to the view in the template itself.
The TargetActionSupport mixin (DelegateSupport in sproutcore, I think) seems like a good candidate, but would still require the target (controller) and action to be set in the template itself.
Ideally, I would like to instantiate my views in my controller (somehow), set targets and actions, but also set presentational variables in my templates (ex: static classes, id), but I'm not sure how to accomplish something this, or whether it's the right approach.
You can programmatically instantiate views and insert them into the DOM wherever you'd like:
var view = Ember.View.create();
view.appendTo('#someElement');
If you want to eliminate global binding paths, you can pass in a controller reference to the view on instantiation:
var controller = Ember.Object.create({content: {}}),
view = Ember.View.create({
controller: controller,
contentBinding: 'controller.content'
});
I'd also suggest looking into the Ember StateManager.

Resources