How to display a fully formed HTML page as an MVC view? - model-view-controller

MVC/ASP.NET/C#/html/javascript newbie question:
I'm trying to move some legacy software into an MVC solution. I have an MVC controller ViewResult method that makes an API call to the legacy system and returns a string which is a fully formed HTML page (including the HTML start and end tags). Some time in the future, I'll rewrite the logic as an MVC view, but for right now I need to just display that page (preferably in a new tab).
I've tried this in the controller:
return View((object)calendar);
(where "calendar" is the string containing the HTML document)
In my view I have
#model string
#{ Layout = null; }
#Model
But that didn't work.
Any ideas?

Model binding is binding the object of your model class.
For example, ([Solution].[Models].[Model class]),
#model PassDatainMVC.Models.Record
To pass the data from controller to view,
Approach 1: ViewBag
Controller:
string data = "testing";
ViewBag.see = data;
return View();
View:
#using PassDatainMVC.Models
#ViewBag.see
Or:
Approach 2: Model binding
Controller (Class):
public string recordProperty;
View:
#model PassDatainMVC.Models.Record
#Model.recordProperty
While you have to set the property under the model class in the data field for the second approach.
Ref. https://www.c-sharpcorner.com/article/asp-net-mvc-passing-data-from-controller-to-view/

If you want to just one data you can use a ViewBag. This is simple.
Also you want to send with model. You should use this code.
Class
public class Calendar
{
public string CalendarName { get; set; }
}
Controller
Calendar newModel = new Calendar();
newModel.CalendarName = "test name...";
return View(newModel);
View
#model ModelNamespace.Calendar
<h1> #Model.CalendarName </h1>

Thanks Reha! But unfortunately neither of your suggestions did the trick.
For your first suggestion I used ViewBag. In the controller I replaced
return View((object)calendar);
to
ViewBag.calendar = calendar;
return View();
And replaced the view with just
#{ Layout = null; }
#ViewBag.calendar
The result was that the user is left looking at the actual HTML code instead of what the HTML code is supposed to render.
For your 2nd suggestion, I did exactly as you suggested (except I changed
Model.CalendarName = "test name...";
to
Model.CalendarName = calendar;
The result is the same, the user is left looking at the HTML code.

Related

Constructing HTML in Controller - how to refactor?

I'm currently generating breadcrumbs on an object's Details page by calling a GetBreadcrumbs() method in the object's controller - in this method, the object's parent/grandparent are used to generate an unordered list. What's the best way to pull the HTML out of the controller to follow the Separation of Concerns paradigm? Should a partial view be used here?
Typical example of partial view is Breadcrum itself. For example, in your controller you can have
//
//GET: News/Article/x
public ActionResult Article(int id)
{
//get parentid of article
ViewBag.id = id;
ViewBag.parentid;
return View();
}
So, your partial view will be as below:
#{
ViewBag.Title = "Article";
}
<h2>Viewing Article #ViewBag.parentid >> #ViewBag.id</h2>
You could use partial views or display templates. Your controller should only build the model that will be passed to the view and then inside the view you could use a display template that will build the desired output based on the model.

Partial View HttpPost invoked instead of HttpGet

I'm working on the admin part of an MVC webapp. I had the idea to use "widgets" for a single Admin panel. I'll explain my intentions first.
I have a languages table, and for that I'd like to create a partial view with a dropdownlist for those languages and a single button "Edit", that would take the user to a non-partial view to edit the language. After clicking save, the users would be redirected to the Index view, which would just show the dropdownlist again.
So I have a "Index.cshmtl", and an "EditLanguage.cshtml" as non-partial views, and a "LanguageWidget.cshtml" as a partial view.
First the user sees the Index view.
public ViewResult Index()
{
return View();
}
This view has the following code in it:
#using CodeBox.Domain.Concrete.ORM
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Administration</h2>
#Html.Action("LanguageWidget")
The Partial view "LanguageWidget" just contains the following code, and when the user submits it posts to the HttpPost annotated method in my controller:
#using (Html.BeginForm("LanguageWidget", "Admin"))
{
#Html.DropDownListFor(model => model.SelectedItem, Model.Languages)
<input type="submit" value="Edit"/>
}
This is the HttpPost method for the widget:
[HttpPost]
public ActionResult LanguageWidget(LanguageWidgetModel model)
{
var lang = langRepo.Languages.FirstOrDefault(l => l.LanguageId == model.SelectedItem);
return View("EditLanguage", lang);
}
This takes the user to the language edit page, which works fine.
But then! The user edits the language and submits the page, which invokes the "EditLanguage" HttpPost method, so the language is saved properly.
[HttpPost]
public ViewResult EditLanguage(Language model)
{
if (ModelState.IsValid)
{
langRepo.SaveLanguage(model);
TempData["message"] = string.Format("{0} has been saved!", model.Name);
return View("Index");
}
else
{
return View(model);
}
}
So, when I return the "Index" view - which seems logical I guess - the controller still assumes this is a HttpPost request, and when it renders the Index view, it invokes the "LanguageWidget" method, assuming it has to render the HttpPost method.
This leads to the LanguageWidget HttpPost method, which returns a full view with layout, returning just that, so I have my layout, with view, containing a layout, with the editview.
I don't really see how I could fix this?
I'm pretty sure it's a design flaw from my part, but I can't figure it out.
Thanks in advance!!
Consider using:
return RedirectToAction("Index")
instead of:
return View("Index");
It might seem more logical if the user is actually redirected to Index instead of
remaining at the EditLanguage. And if the user hits the refresh button no data will be resent using this approach.

How do I insert a partial view in a View at a certain place in the view with MVC3?

I have an MVC3 application that I am implementing pjax into . Everything is working well except what to do on the server side when an address gets loaded that doesn't already have the main view on the client side. My Controller code looks like
public virtual ActionResult Details(Guid id)
{
var partDetail = new PartDetail(id);
var partialView = PartialView("Details", partDetail);
if(Request.Headers["X-PJAX"]!= null)
{
return partialView;
}
var mainView = View("Index");
// Stick Partial View into main view at #update_panel?
return mainView;
}
How can I stick the partial View into the main view so it inserts the partial view in the #update_panel?
Ok, without a major refactor, you could do the following.
(this assumes that you are able to set the #model on index.cshtml to PartDetail()).
in your controller action above, change:
var mainView = View("Index");
to:
var mainView = View("Index", partDetail);
then, inside your index.cshtml, add the following:
<div id="update_panel">#RenderPartial("Details", Model)</div>
As i said, this will ONLY work if the index #model is set to PartDetail(), otherwise, a little refactoring on the model in the index view will be required to include this PartDetail() model. this viewmodel might well look like the following:
public class IndexViewModel
{
ModelForIndex Index{get; set;}
PartDetail Details{get; set;}
}
this refactored viewmodel would be added to the index.cshtml as #model IndexViewModel and consumed by the partial as:
<div id="update_panel">#RenderPartial("Details", Model.Details)</div>
hope this makes sense.

MVC3: How to Post a Form that contains partial views?

Tools: MVC3, jQuery Unobtrusive Validation, Razor, VS 2010
I am developing an MVC2 project that enables users to request services. I have placed information common to all forms in partial views, which are strongly typed to their own models. Each partial view has its own controller. The partial views are rendered in the main container page. I have unobtrusive jQuery data validation working for all data on the rendered page.
Questions: What is the best way to code a Post that relays all the page data to the server and how can I associate the partial views to their respective models? Is it possible for the controllers for the partial views to handle their own data storage chores? Any good examples somewhere? Or, is this architecture flawed and I should rethink?
Thanks in advance,
Arnold
No, not at all, sounds nicely broken up and easy to test. First off, make sure the forms are well set up with the right action, method, etc. in HTML. So then to post the whole page you could do something like this:
var savePage = function () {
$('form').each(function (formIndex, formElement) {
var f = $(formElement);
$.post(f.attr('action'), f.serialize(), successfulFormPost);
});
};
var successfulFormPost = function (data) { ... };
Now, if your MVC view looks something like this:
(Notice the naming convention for the name attribute). Then you can make your controller for that form take in a strongly typed parameter that matches the view's #Model:
public class SomeModel {
public int Id { get; set; }
public string Description { get; set; }
}
public class SomeController : Controller {
[HttpPost]
public ActionResult SomeAction(SomeModel someModel) {
// use someModel.Id, someModel.Description here
}
}
I did that HTML a little more manually, but I'm just proving a point about binding and linking up HTML POST with controller actions. I'll leave it up to you to bring in unobtrusive validation by using the Html.TextBox type syntax. Just remember to set the name attribute of your input fields according to how the default binder works:
http://www.asp.net/mvc
That's a great source for all these fundamentals.

Modifying MVC 3 ViewBag in a partial view does not persist to the _Layout.cshtml

I am using MVC 3 with the Razor view engine. I want to set some values in the ViewBag inside a Partial View and want retrieve those values in my _Layout.cshtml. For example, when you setup a default ASP.NET MVC 3 project you get a _Layout.cshtml file in the "/Views/Shared" folder. In that _Layout.cshtml the Page Title is set like this:
<title>#ViewBag.PageTitle</title>
Then in "/Views/Home/About.cshtml" view the contents of the ViewBag are modified:
#{
ViewBag.Title = "About Us";
}
This works fine. When the About view is rendered the page title is "About Us". So, now I want to render a Partial view inside my About view and I want to modify the ViewBag.Title inside my Partial view. ("/Views/Shared/SomePartial.cshtml")
#Html.Partial("SomePartial")
In this Partial view I have this code:
#{
ViewBag.Title = "About Us From The Partial View";
}
When I debug this code I see the ViewBag.Title get set to "About Us" and then in the Partial view I see it get reset to "About Us From The Partial View", but when the code hits the _Layout.cshtml it goes back to "About Us".
Does this mean that if the contents of the ViewBag are modified in a Partial view, those changes will not appear(be accessible) in the main view (About.cshtml) or the _Layout.cshtml?
Thanks in advance!
If you pass the ViewBag into the partial's viewdatadictionary, then pull it out (and cast), you can do whatever you want and the reference is kept. The cool part is that since it's dynamic, you can even add properties and then they'll show up on the parent page's Viewbag.
Page:
//set the viewbag into the partial's view data
#{Html.RenderPartial("Elaborate", Model, new ViewDataDictionary { {"vb", ViewBag}});}
Partial:
#{
var vb = ((dynamic)ViewData["vb"]);
vb.TheTitle = "New values";
}
Page
#ViewBag.TheTitle = "New value"
I also had this problem, and couldn't find any neat and obvious solution.
The solution I came up with was to implement an Html extension method that returns a 'PageData' class that you define, containing whatever data you need:
[ThreadStatic]
private static ControllerBase pageDataController;
[ThreadStatic]
private static PageData pageData;
public static PageData GetPageData(this HtmlHelper html) {
ControllerBase controller = html.ViewContext.Controller;
while (controller.ControllerContext.IsChildAction) {
controller = controller.ControllerContext.ParentActionViewContext.Controller;
}
if (pageDataController == controller) {
return pageData;
} else {
pageDataController = controller;
pageData = new PageData();
return pageData;
}
}
It finds the top-level controller for the current request, and returns the same PageData object every time the method is called within the same HTTP request. It creates a new PageData object the first time it is called in a new HTTP request.
The partial view gets its own ViewBag.
You can get the page's ViewBag from ((WebViewPage) WebPageContext.Current.Page).ViewBag
You can do this trick in your partial view to override the title in your _Layout.cshtml:
#{
ViewBag.Title = "About Us From The Partial View";
}
......
<script type="text/javascript">
document.title = "#ViewBag.Title";
</script>
As others have pointed out Layout, Views and Partials get their own ViewBag. However, I was able to get it to work with the following:
In the View or Partial.
#{ Html.ViewContext.ViewBag.Title = "Reusable Global Variable"; }
Then in _Layout
#Html.ViewContext.ViewBag.Title
By explicitly using the ViewContext, the views 're-use' the same ViewBag.
If anyone is still looking for a solution to this it appears that you can do it with TempData:
TempData["x"] = x;
TempData is persisted until it is read so you can just read it in your _Layout. You just have to be careful that you read everything so that it is cleared for the next request.
I've tried the following and it works:
In the (parent) view...
#Html.Partial("SomePartial", ViewData, null)
Note: ViewData is passed as the model argument, but you have to specify null for the viewData argument to use the correct overload. You can't use ViewBag because Html.Partial doesn't like dynamics.
Then , in the partial view...
#model ViewDataDictionary
#{
Model["Title"] = "About us from the partial view";
}
Of course, if you need to use the model argument for a real model, you'll have to be more creative.
try #SLaks code with
(((WebViewPage) WebPageContext.Current.Page).ViewBag).PropertyName
I encountered the same problem when I use mvc3, and I found that
this.ViewBag.ViewBag.PropertyName
works in your custom control.
I this is what page data is designed for. Pop this into your view.
#Page.somePropertyName = "Whatever you want";
And then access it in your layout view. Be sure to check that its not null first.
#{
if(Page.somePropertyName != null)
{
//Do stuff
}
}

Resources