Easiest way to update an MVC View from a Controller - c#

I am totally new to .NET's MVC framework and have a scarce understanding of how the Model-View-Controller architecture works. I have a simple and basic problem.
In my application, it is a calendar app, I have a Controller named EventsController which overrides a method named OnCommand, which is a part of the DayPilot Calendar(open source calendar). Here's my method:
protected override void OnCommand( CommandArgs e )
{
switch (e.Command)
{
case "previous":
StartDate = StartDate.AddMonths(-1);
StrMonth = CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedMonthName(StartDate.Month);
Console.WriteLine(StrMonth);
Query();
Update(CallBackUpdateType.Full);
break;
case "next":
StartDate = StartDate.AddMonths(1);
StrMonth = CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedMonthName(StartDate.Month);
Console.WriteLine(StrMonth);
Query();
Update(CallBackUpdateType.Full);
break;
}
}
When a forward and backwards arrow are clicked at the top of the calendar - the previous and next are called and StrMonth is set to the correct month. There is a div containing the name of the month between the two arrows, it needs to update from the Controller Method or another. How do I update the view from the Controller to correctly change the month displayed at the top of the calendar?
I have tried the following:
-Adding a property to my Event Model named CurrentMonth and then using that variable inside of the div in the View like this:
<div>#Model.CurrentMonth</div>
and then attempting to use an ActionResult in my Controller that updates it for me and calls a Javascript in my View:
In the Controller:
public ActionResult UpdateMonth(Event model)
{
model.CurrentMonth = "changeMonth()";
//return View(model);
}
In the View:
<script type="text/javascript">
#Model.Item2.CurrentMonth;
function changeMonth() {
#ViewBag.strMonth = #Model.Item2.CurrentMonth;
}
</script>
This should be trivial, in almost every other client-server architecture, updating the client is much easier than in MVC. What is the simplest and most basic way to update the View based on a Controller ActionResult or method? Do I need to be using Ajax or the ViewBag/ViewData properties of MVC? An example would be greatly appreciated.

Related

Render custom controls in mvc in C#

I have a need to display many different data types on an MVC page using C#.
What is the best way to code this up. I have 200 items with 10-15 different data types I want to be able to edit.
I could put all of the code in the cshtml but would prefer to, as I have with the original php code, call a function to render the controls.
if(controlType = "checkBox"
{
...
}
else if(controlType = "string"
{
...
}
else if(controlType = "myClassType"
}
Thanks
Use Html.EditorFor. It will look for a matching template depending on the datatype of the property in the ViewModel in the Views\Shared\EditorTemplates folder.
For example, you can define a template Boolean.cshtml to render a checkbox for boolean values of your model.
See Using Display Templates and Editor Templates in ASP.NET MVC.
The EditorFor() helper displays a user interface for editing a model property. This user interface is known as Editor Template.
Check the below example for more details,I have Index() action,,return a list of object with different datatypes
public ActionResult Index()
{
NorthwindEntities db=new NorthwindEntities();
Employee emp = db.Employees.Find(1);
return View(emp);
}
The preceding code retrieves an Employee from the database whose EmployeeID is 1. It then passes this Employee object to the Index view. The Index view contains the following markup:
#model Demo.Models.Employee
#{
Layout = null;
}
...
<body>
#using(Html.BeginForm())
{
#Html.DisplayFor(m=>m.EmployeeID)
#Html.DisplayFor(m=>m.FirstName)
#Html.DisplayFor(m=>m.BirthDate)
}
</body>
</html>
It then uses the DisplayFor() helper to show three model properties
EmployeeID, FirstName, and BirthDate.

ASP.NET MVC4 - Multiple models of same type with partial views on the same page

I have a single page ASP.NET MVC 4 application which should display charts. Each chart (dotnetHighcharts) is contained in a single partial view with its own model.
Now I want to be able to display multiple charts on a single page, each with its own model, rendered in a partial view.
My controller looks like this:
public class ReportController : Controller
{
[HttpGet]
public ActionResult Display(Guid? id)
{
var viewModel = PrepareViewModel(id);
return View(viewModel);
}
[HttpPost]
public ActionResult DisplayChart(Guid? id)
{
var viewModel = PrepareViewModel(id);
return PartialView("Partial_ChartView", viewModel);
}
}
The partial view looks like this:
#model MobileReports.Models.ReportViewModel
#Model.Chart
The main view has this code:
#model IEnumerable<MobileReports.Models.ReportViewModel>
#{
ViewBag.Title = "Display";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div id="chartContainer" style="float:left">
#{Html.RenderPartial("Partial_ChartView");}
</div>
<div id="secondChartContainer" style="float:right">
#{Html.RenderPartial("Partial_ChartView");}
</div>
The Model itself contains only a rendered chart and a few infos about it.
My problem is, I don't know if I am doing it right. The mainview has to know which charts are already displayed on the page, so that I dont display the same chart twice. In the initial load, I somehow have to tell the partialviews which charts are already being displayed on the page. How can I do that?
Also how do I keep multiple models on the main view?
-edit-
The controller action DisplayChart(Guid? id) is used inside the partialView from a javascript function:
function displayNextReport() {
// try to find a report that is not displayed already
var diff = $(availableReports).not(displayedCharts).get();
if (diff.length > 0) {
// remove old chart data
if ($("#chart_container").highcharts() !== undefined) {
$("#chart_container").highcharts().destroy();
}
// load the new report
$("#chart_container").load('#Url.Action("DisplayChart", "Report")', { id: diff[0] });
}
}
Note: This does not work right now. I need to pass an argument that tells the DisplayChart() method which chart to load next. For this, I need to know which charts are currently diplayed.
My code is most likely very wrong and messed up. I think it is best to just describe what I want to achieve:
I want to have a View that displays multiple partial views of the same type. Each partial view has to know what the other partial view are currently displaying. Thats all I want to do..

Pass variable back from EditorTemplate to page or layout

I am tired to google around and ddn't found the exact answer.
I know I can pass date from view to template by additionalViewData parameter of .EditorFor() or .DisplayFor() but I want a data back from template!
I have Date.cshtml in my ~/Views/Shared/EditorTemplates/.
My model property already have something like this:
public class Person
{
[DisplayFormat(DataFormatString = "{0:d}")]
[DataType(DataType.Date)]
public Nullable<DateTime> BirthDate { get; set; }
}
and the view:
#Html.EditorFor(model => model.BirthDate)
Now I want only to load datepicker script when ever the view uses the date template.
So I try to use RequireDatePicker like this in my template:
#model DateTime?
#Html.TextBox(...)
#{ViewBag.RequireDatePicker = true;}
and in the bottom of _Layout.cshtml:
#Scripts.Render("~/bundles/jquery")
#if (ViewBag.RequireDatePicker == true)
{
<script type="text/javascript">
if (!Modernizr.inputtypes.date) {
// Todo: use Modernizr.load
$(function () {
$("input[type='date']").datepicker();
});
}
</script>
}
The problem is I just realize ViewBag/ViewData has a different context from the view/partial and the display/editor template. What is the best way to do this?
Fix
I can pass data from template back to view by route data like this:
#{ViewContext.RouteData.DataToken["requireDatePicker"] = true;}
However I end-up not passing the data but just this:
Have bundle "~/bundles/jqueryui" that
remove jquery ui date picker feature from it (and move to "~/bundles/jqueryui-datepicker")
include a Modernizr.inputtypes.date check and Modernizr.load to load "~/bundles/jqueryui-datepicker"
No need to modify _layout.cshtml to check for RequireDatePicker. #Scripts.Render("~/bundles/jquery") and #Scripts.Render("~/bundles/jqueryui") is enough sinec Modernizr.load is already included.
No need to pass RequireDatePicker from Date.cshtml to view or _Layout.cshtml as Modernizr.load
You could create dynamic javascript bundles like outlined in the following article
http://neimke.blogspot.co.nz/2011/12/composing-child-views-with-bundles.html
basically your editorfor template calls the following code
#{
var bundle = Microsoft.Web.Optimization.BundleTable.Bundles.GetRegisteredBundles()
.Where(b => b.TransformType == typeof(Microsoft.Web.Optimization.JsMinify))
.First();
bundle.AddFile("~/Scripts/DatePicker.js");
}
but you may want to replace the Where clause with one that finds a specific bundle instead, maybe one specifically for including javascript for child views.
If you wanted to go nuts, you could probably extend the razor view engine or WebViewPage to look for matching View.cshtml.js to every view that is rendered and add it to a bundle.

Html.HiddenFor binding to wrong element

I have an ASP.NET MVC application that displays a list of items. In my view page I loop over the items and render each item with partial view, like so:
#foreach(var item in Model.items)
{
<li>
#Html.Partial("ItemView", item)
</li>
}
In the item view, I wrap each item with a form that has a 'Delete' button, like this:
#using(Html.BeginForm(...))
{
#Html.HiddenFor(m=>m.Id)
<label>#Model.Name (#Model.Id)</label>
<input type="submit" value="Delete"/>
}
The items are rendered properly, the resulting page has a nice list of all the items with their proper names and IDs displayed.
EDIT: The same happens with #Hidden, apparently, contrary to what I wrote before.
In addition, this only happens the second time the form is rendered (that is, after one of the Delete buttons is clicked), the first time everything is working properly. My action methods looks like this:
public ActionResult AllItems()
{
var model = new AllItemsModel();
return PartialView(model);
}
public ActionResult Delete(DeleteModel model)
{
.... Perform the delete ...
return PartialView("AllItems", new AllItemsModel());
}
Why is this happening?
I suspect that this happens because you already have an Id parameter in your RouteData:
public ActionResult SomeAction(int id)
{
var model = ...
return View(model);
}
and you have requested the page with /somecontroller/someaction/123. The HiddenFor helper now uses the Id from the route values and not the id of the item. Try renaming the property on your item view model to something different than id. For example ItemId.
Another possibility is that the problem occurs only after the postback and not when the page is initially rendered. Showing your POST action might help in exploring this possibility further.
UPDATE:
Alright, now that you have shown your POST action things are much more clear:
public ActionResult Delete(DeleteModel model)
{
.... Perform the delete ...
return PartialView("AllItems", new AllItemsModel());
}
you are basically creating a new view model here and passing it to the partial view. But HTML helpers always use the value from the ModelState when binding. And only after that the value from your view model. So if you intend to modify properties on your model inside your POST action make sure that you have removed this value from the ModelState first. In your example since you have completely scratched the entire view model (by creating a new AllItemsModel()) you could clear the entire ModelState:
public ActionResult Delete(DeleteModel model)
{
.... Perform the delete ...
// Clear the modelstate otherwise the view will use the values that were initially posted
// and not the values from your view model
ModelState.Clear();
return PartialView("AllItems", new AllItemsModel());
}
This behavior is by design and applies to all HTML helpers, not only the HiddenFor helper.

Multiple models with ID in one view

Working on this project and I am stuck again. I have a view that I need to display a menu along the side. The menu is pulled from a table. Along the side of this, I have a question that is pulled from another table. Inside of this, there will be suggestions that are yet pulled from another table. With the question, I have a couple of inputs, radio buttons, and a text box for feedback which will end up in a different table. All of this has to take place with the ID from the main table so that when the information is saved in the results table it knows what the main record it belongs too. The page can be brought up with a /Audit/302 but when you change the submenu to the next question how do you get that question and maintain the ID. For Every question, there will be an entry for that ID. I have made relationships this is what it looks like.
August 02 2011
I have added a ViewModel that pulls a list of checkboxes from a table called Score (OK, Concern, N/A)
The page that i have is rendered with an ID it pulls a record from AuditSchedules and retrieves some info. In the Layout i also have a side menu (partial) that renders a list of actionlinks that will select different sections and a MainQuestion (partial). We work up to this point. Since i have added the viewModel to populate the checkboxes and save the text into a table with the ID of the AuditSchedule and MainQuestion. My issue is i am calling for id to select the ids from the table for the Scores and it is grabbing the id from the AuditSchedule. Giving me a no data found.
Here is my Controller:
//get
public ActionResult _Forms(int score)
{
AuditFormEdit viewModel = new AuditFormEdit();
viewModel.ScoreInstance = _db.Scores.Single(r => r.ScoreID == score);
viewModel.InitializeScoreCheckBoxHelperList(_db.Scores.ToList());
return View(viewModel);
}
//post
[HttpPost]
public ActionResult _Forms(int score, AuditFormEdit viewModel)
{
if (ModelState.IsValid)
{
viewModel.PopulateCheckBoxsToScores();
_db.Entry(viewModel.ScoreInstance).State = System.Data.EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("/");
}
else
{
return View(viewModel);
}
}
Here is my viewModel:
public class AuditFormEdit
{
public Models.Score ScoreInstance { get; set; }
public List<ScoreCardCheckBoxHelper> ScoreCardCheckBoxHelperList { get; set; }
public void InitializeScoreCheckBoxHelperList(List<Models.Score> ScoreList)
{
if (this.ScoreCardCheckBoxHelperList == null)
this.ScoreCardCheckBoxHelperList = new List<ScoreCardCheckBoxHelper>();
if (ScoreList != null
&& this.ScoreInstance != null)
{
this.ScoreCardCheckBoxHelperList.Clear();
ScoreCardCheckBoxHelper spacerProductCheckBoxHelper;
string spacerTypes =
string.IsNullOrEmpty(this.ScoreInstance.ScoreName) ?
string.Empty : this.ScoreInstance.ScoreName;
foreach (Models.Score scoreType in ScoreList)
{
spacerProductCheckBoxHelper = new ScoreCardCheckBoxHelper(scoreType);
if (spacerTypes.Contains(scoreType.ScoreName))
spacerProductCheckBoxHelper.Checked = true;
this.ScoreCardCheckBoxHelperList.Add(spacerProductCheckBoxHelper);
}
}
}
public void PopulateCheckBoxsToScores()
{
this.ScoreInstance.ScoreName = string.Empty;
var scoreType = this.ScoreCardCheckBoxHelperList.Where(x => x.Checked)
.Select<ScoreCardCheckBoxHelper, string>(x => x.ScoreName)
.AsEnumerable();
this.ScoreInstance.ScoreName = string.Join(", ", scoreType);
}
public class ScoreCardCheckBoxHelper : Models.Score
{
public bool Checked { get; set; }
public ScoreCardCheckBoxHelper() : base() { }
public ScoreCardCheckBoxHelper(Models.Score scoreCard)
{
this.ScoreID = scoreCard.ScoreID;
this.ScoreName = scoreCard.ScoreName;
}
}
}
Here is the link and controller that pulls the page up this is the Body of the layout:
public ActionResult Audit(int id)
{
var auditToDetail = _db.AuditSchedules.Single(r => r.AuditScheduleID == id);
return View(auditToDetail);
}
the link looks like this:
/AuditSchedule/Audit/257
Let me know if you need anything else.
I have used a code first approach and all tables have models with the relationships called out.
thank you
Your question is pretty general - but here are some things to think about.
First, in MVC3 you typically want to strongly type your views to your models. In your case you would want a single view for each model. You don't want "multiple multiple models in a view", but rather multiple views rendered together on a single page - all linked by some common data.
One way to accomplish this would be set write a "child actions" (use:[ChildActionOnly] attribute) in your controller for populating each of your individual models sending them to their respective indiviudal views. You can use the [Html.RenderAction][3] function within any view to render any other view. The easiest way to organize this is often with layouts 4 - You could render all your views using Html.RenderAction(), and then render your main form as the primary view (displayed in the layout with #RenderBody()).
To populate each of your models you will likely need to know some data (like an id), these can be stored in the ViewBag or ViewData, and passed in as objects in the Html.RenderAction function. (If you are using razor the call in your view might look something like:#{Html.RenderAction("actionName", new {object1 = "xyz", object2 ="abc"});} Just to make this clear, the logical flow will look like this
Populate the ViewBag with the data you will need to render your sub-views,
Populate your model for the main form and you call the main form's view (your primary view).
Your primary view defines a layout view, which in-turn calls Html.RenderAction, using the ViewBag data to set the appropriate objects.
The layout also renders the primary view as the "body" (#RenderBody()).
The end result of this is views that can be [strongly typed][7] to individual views, and individual controller actions for each model. This is a good thing, because it makes your views reusable anywhere, and it isolates the logic for populating each model (because each model has its own action in the controller). If you make subsequent changes to a model, it will be easy to know which Action/Model/View needs to be modified.
Below are some resources to get you started in understanding your options for rendering multiple models on a single page. I apologize for not lacing this post with nice links to helpful items, but as a new answerer, I was limited to two... Google will have to be your guide for the others.
Hope this helps - Best of Luck!
Resources:
Html.RenderAction and Html.Action - http://haacked.com blog
When to use Html.RenderPartial and Html.RenderAction in ASP.NET MVC Razor Views - http://www.arrangeactassert.com

Categories