I am trying to parse data from my ViewData but its not going so well.
Controller:
public ActionResult ListFilm()
{
MediaCatalog mediaCL = new MediaCatalog();
// Retrieve a list of film media's
List<CatalogDb.FilmMedia> listFilmMedia = new List<CatalogDb.FilmMedia>();
listFilmMedia = mediaCL.GetAllFilmMediaTitles();
ViewData["FilmList"] = listFilmMedia.ToList();
return View();
}
View:
<% foreach(var item in (ViewData["FilmList"] as List<CatalogDb.FilmMedia>)) { %>
<%=item.title %>
<% } %>
I noticed that the code written in my view file is parsed directly in the source, so for some reason its not being executed as code?
Since you are using razor view engine, your syntax is incorrect and your code is not parsed.
In razor view engine you would have to write something like
#foreach (var item in (ViewData["FilmList"] as List<CatalogDb.FilmMedia>))
{
#item.title
}
What you've written there is web forms view engine syntax. You could either switch to that view engine or write code in razor syntax for your code to be parsed and executed.
I personally use the ViewBag property, it has been added to ASP.NET MVC3. It uses the dynamic under the hood, which makes the ViewBag also strongly typed. (ViewData uses a dictionary under the hood and thus always returns an object , which you have to cast).
So basically, instead of using ViewData["MyPropery"] as MyType, you can directly call ViewBag.MyProperty and you don't need to cast it (to "MyType").
Related
I am migrating an ASP.NET application to ASP.NET Core and they have some calls to HttpServerUtility.Transfer(string path). However, HttpServerUtility does not exist in ASP.NET Core.
Is there an alternative that I can use? Or is Response.Redirect the only option I have?
I want to maintain the same behaviour as the old application as much as possible since there is a difference in between Server.Transfer and Response.Redirect.
I see some options for you, depending on your case:
Returning another View: So just the HTML. See answer of Muqeet Khan
Returning another method of the same controller: This allows also the execution of the business logic of the other action. Just write something like return MyOtherAction("foo", "bar").
Returning an action of another controller: See the answer of Ron C. I am a bit in troubles with this solution since it omits the whole middleware which contains like 90% of the logic of ASP.NET Core (like security, cookies, compression, ...).
Routing style middleware: Adding a middleware similar to what routing does. In this case your decision logic needs to be evaluated there.
Late re-running of the middleware stack: You essentially need to re-run a big part of the stack. I believe it is possible, but have not seen a solution yet. I have seen a presentation of Damian Edwards (PM for ASP.NET Core) where he hosted ASP.NET Core without Kestrel/TCPIP usage just for rendering HTML locally in a browser. That you could do. But that is a lot of overload.
A word of advice: Transfer is dead ;). Differences like that is the reason for ASP.NET Core existence and performance improvements. That is bad for migration but good for the overall platform.
You are correct. Server.Transfer and Server.Redirect are quite different. Server.Transfer executes a new page and returns it's results to the browser but does not inform the browser that it returned a different page. So in such a case the browser url will show the original url requested but the contents will come from some other page. This is quite different than doing a Server.Redirect which will instruct the browser to request the new page. In such a case the url displayed in the browser will change to show the new url.
To do the equivalent of a Server.Transfer in Asp.Net Core, you need to update the Request.Path and Request.QueryString properties to point to the url you want to transfer to and you need to instantiate the controller that handles that url and call it's action method. I have provided full code below to illustrate this.
page1.html
<html>
<body>
<h1>Page 1</h1>
</body>
</html>
page2.html
<html>
<body>
<h1>Page 2</h1>
</body>
</html>
ExampleTransferController.cs
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace App.Web.Controllers {
public class ExampleTransferController: Controller {
public ExampleTransferController() {
}
[Route("/example-transfer/page1")]
public IActionResult Page1() {
bool condition = true;
if(condition) {
//Store the original url in the HttpContext items
//so that it's available to the app.
string originalUrl = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.Path}{HttpContext.Request.QueryString}";
HttpContext.Items.Add("OriginalUrl", originalUrl);
//Modify the request to indicate the url we want to transfer to
string newPath = "/example-transfer/page2";
string newQueryString = "";
HttpContext.Request.Path = newPath;
HttpContext.Request.QueryString = new QueryString(newQueryString);
//Now call the action method for that new url
//Note that instantiating the controller for the new action method
//isn't necessary if the action method is on the same controller as
//the action method for the original request but
//I do it here just for illustration since often the action method you
//may want to call will be on a different controller.
var controller = new ExampleTransferController();
controller.ControllerContext = new ControllerContext(this.ControllerContext);
return controller.Page2();
}
return View();
}
[Route("/example-transfer/page2")]
public IActionResult Page2() {
string originalUrl = HttpContext.Items["OriginalUrl"] as string;
bool requestWasTransfered = (originalUrl != null);
return View();
}
}
}
Placing the original url in HttpContext.Items["OriginalUrl"] isn't strictly necessary but doing so makes it easy for the end page to know if it's responding to a transfer and if so what the original url was.
I can see this is a fairly old thread. I don't know when URL Rewriting was added to .Net Core but the answer is to rewrite the URL in the middleware, it's not a redirect, does not return to the server, does not change the url in the browser address bar, but does change the route.
resources:
https://weblog.west-wind.com/posts/2020/Mar/13/Back-to-Basics-Rewriting-a-URL-in-ASPNET-Core
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/url-rewriting?view=aspnetcore-5.0
I believe you are looking for a "named view" return in MVC. Like so,
[HttpPost]
public ActionResult Index(string Name)
{
ViewBag.Message = "Some message";
//Like Server.Transfer() in Asp.Net WebForm
return View("MyIndex");
}
The above will return that particular view. If you have a condition that governs the view details you can do that too.
I know that this is a very old question, but if someone uses Razor Pages and is looking to a Server.Transfer alternative (or a way to return a different view depending on a business rule), you can use partial views.
In this example, my viewmodel has a property called "UseAlternateView":
public class TestModel : PageModel
{
public bool UseAlternateView { get; set; }
public void OnGet()
{
// Here goes code that can set UseAlternateView=true in certain conditions
}
}
In my Razor View, I renderize a diferent partial view depending of the value of the UseAlternateView property:
#model MyProject.Pages.TestModel
#if (Model.UseAlternateView)
{
await Html.RenderPartialAsync("_View1", Model);
}
else
{
await Html.RenderPartialAsync("_View2", Model);
}
The partial views (files "_View1.cshtml" and "_View2.cshtml"), contain code like this:
#model MyProject.Pages.TestModel
<div>
Here goes page content, including forms with binding to Model properties
when necessary
</div>
Obs.: when using partial views like this, you cannot use #Region, so you may need to look for an anternative for inserting scripts and styles in the correct place on the master page.
I've trying to send a file through Ajax.beginForm with enctype = "multipart/form-data", but I always get it as null,
I'm returning a JavaScriptResult from My controller like this,
public JavaScriptResult SaveFile(MyModel model)
{
JavaScriptResult result = new JavaScriptResult();
result.Script = "$('#ShowPopup').trigger('click')";
return result;
}
When using Html.BeginForm it works great, but when it returns the result, it shows the following on the browser,
I don't know what's wrong with Html.BeginForm(), also when I'm returning a view, the view returns but without the my _Layout.cshtml page, breaks my design,
so the problem is when I use ajax it perfectly great but doesn't post file,
and when I'm using html.beginForm, it posts the file, but breaks the design after return.
I'm a bit confused.
I have a pretty standard MVC3 setup: Home Controller/Index method, Strongly typed view to a ViewModel.
My ViewModel is a collection of three generic lists (Schemes, Types, Tables),
TableModel
Schemes List
TypesList
TablesList
Schemes and Types populate dropdowns to act as filters on the third (Tables) collection which populates a HTML table via a #foreach loop
I populate these via a call to Home/Index (no arguments). This works fine. Now I want to mark a number of rows from the TablesList collection for deletion by checking a checkbox and then, on clicking Submit, I want the deletion to be actioned via a Post and the table contents to be refreshed with the remaining tables back into the same place (replacing the existing HTML table
In my Index View I have this section:
#using (Html.BeginForm("DeleteTables", "Home"))
{
#Html.Hidden("tableList", #Model.TablesList)
<input id="deleteButton" type="submit" />
}
in my HomeController I have a POST method as follows
[HttpPost]
public ActionResult DeleteTables(List<ViewModel.ITableInformation> tableList)
{
var deleteableTableIds = from t in tableList
where t.DeleteTable
select t.TableId;
this.tableModel.DeleteTablesById(deleteableTableIds.ToList());
.... execute a db call to get remaining tables and return a PartialView or JSON
}
Problem is I'm expecting the tableList argument to this function to contain my model but it's empty.
Any idea what I'm doing wrong - pretty new to this I'm afraid.
Thanks
The hidden can't take a complex object; what you would need to do is deserialize each object and store it in the hidden as a string, something like:
#{
var tableIDs = "";
foreach (var i in Model.TablesList) {
tableIDs = //serialized object in proper format
}
}
#Html.Hidden("tableList", #tableIDs)
From the server, you can create the objects through a custom model binder, or accept an input of type string, and deserialize the collection.
I use ASP.NET MVC for serving web application and I want to create something like the following code.
<% using(HTML.Form(Model)) { %>
<% HTML.CreateTextBox('txt1', x => x.Property1);
<% }
From the above code, Form extension method will receive object that represent type of current Model object in current View page. Next, CreateTextBox method will receive type from Form method and I can bind textbox to some property of this model.
Update 1
The following code is code of CreateTextBox method that will create instance of TextBox class.
public static CreateTextBox(string value, Expression<Func<object>> bindedProperty)
{
// T should be receive from HTML.Form method or class
return new TextBox<T>(value);
}
Is it possible to creating some code that doing something like the above code?
Thanks,
It's not necessary. The model is always going to be available, so you will always be able to do this:
<%
using(Html.BeginForm())
{
Html.TextBox('txt1', Model.Property1);
}
%>
The Model property on your page is always typed (assuming you are using System.Web.Mvc.ViewPage<T>) so you will always have access to the properties directly through the Model property.
If you aren't using the generic version (System.Web.Mvc.ViewPage<T>) then you should probably use that instead of the non-generic version.
When a controller renders a view based on a model you can get the properties from the ViewData collection using the indexer (ie. ViewData["Property"]). However, I have a shared user control that I tried to call using the following:
return View("Message", new { DisplayMessage = "This is a test" });
and on my Message control I had this:
<%= ViewData["DisplayMessage"] %>
I would think this would render the DisplayMessage correctly, however, null is being returned. After a heavy dose of tinkering around, I finally created a "MessageData" class in order to strongly type my user control:
public class MessageControl : ViewUserControl<MessageData>
and now this call works:
return View("Message", new MessageData() { DisplayMessage = "This is a test" });
and can be displayed like this:
<%= ViewData.Model.DisplayMessage %>
Why wouldn't the DisplayMessage property be added to the ViewData (ie. ViewData["DisplayMessage"]) collection without strong typing the user control? Is this by design? Wouldn't it make sense that ViewData would contain a key for "DisplayMessage"?
The method
ViewData.Eval("DisplayMessage")
should work for you.
Of course after I create this question I immediately find the answer after a few more searches on Google
http://forums.asp.net/t/1197059.aspx
Apparently this happens because of the wrapper class. Even so, it seems like any property passed should get added to the ViewData collection by default.
I really need to stop answering my own questions :(