ASP.NET MVC Areas - ActionLink and RedirectToAction - asp.net-mvc-3

While using Areas in ASP.NET MVC 3 project, I stumbled across this problem to do with ActionLink and RedirectToAction method.
I added the following code in the AccountController which is at the Root level ...
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
if (Roles.Provider.IsUserInRole(model.UserName, "Admin"))
{
return RedirectToAction("Index", "Admin", new { area = "Admin" });
}
else
{
return RedirectToAction("Index", "Home");
}
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
Based on the Role which the currently logging in User belongs to, I redirect to the appropriate area. It's working correctly up to this point.
The Admin Area looks as follows ...
In this area, I had copied the _ViewStart.cshtml from the root.
The links for Log Off, About, Home etc dont work as the route which they point to doesn't exist.
I don't want to create another Account, or Home controller in the Areas folder. I would like to use the one in the root.
Following the advice received, on changing the _LogOnPartial.cshtml code as shown ...
#if(Request.IsAuthenticated) {
<text>Welcome <strong>#User.Identity.Name</strong>!
[ #Html.ActionLink("Log Off", "LogOff", "Account", new { area = "" }) ]</text>
}
else {
#:[ #Html.ActionLink("Log On", "LogOn", "Account", new { area = "" }) ]
}
produces the following URL ...
which is still not right.

Area at the rool level will be new { area = "" }. The empty string.

Improving the solution suggested by gdoron and Jasen by changing the _LogOnPartial.cshtml code as follows works ...
#if(Request.IsAuthenticated) {
<text>Welcome <strong>#User.Identity.Name</strong>!
[ #Html.ActionLink("Log Off", "LogOff", "Account", new { area = "" }, null) ]</text>
}
else {
#:[ #Html.ActionLink("Log On", "LogOn", "Account", new { area = "" }, null) ]
}
Likewise, also changed the ActionLink parameters for Home and About menu items in _Layout.cshtml as follows ...
<div id="menucontainer">
<ul id="menu">
<li>#Html.ActionLink("Home", "Index", "Home", new { area = ""}, null)</li>
<li>#Html.ActionLink("About", "About", "Home", new { area = "" }, null))</li>
</ul>
</div>
The links appear correct and work now ...

Related

DropDownList doesn't pass additional parameter

I would like to pass a number selected from DropDownlist to a GET Create method in other Controller. It looks like:
[HttpGet]
public ActionResult Details(int? id, string error)
{
{...}
var numbers = Enumerable.Range(1, 100);
ViewBag.Quantity = numbers.Select(i => new SelectListItem { Value = i.ToString(), Text = i + "%" });
return View(viewModel);
}
public ActionResult Create(int ID, int quantity)
{
{...}
}
Details View looks like:
<div>
#if (Model.ItemRent.Zatwierdzony == false)
{
using (Html.BeginForm("Create", "ItemRentLists", new { ID = #Model.ItemRent.ItemRentID }, FormMethod.Get))
{
#Html.DropDownList("quantity", new SelectList(ViewBag.Quantity, "Text", "Value"))
<input type="submit" value="Dodaj przedmioty"/>
}
}
</div>
DropDownList doesn't pass a Value to "quantity" parameter in Create method, what is wrong here?
OK I changed #Html.DropDownList("quantity", ViewBag.Quantity as SelectList) and now it works as it should work.

How to redirect to Details View?

This is my LearnController and here is the create.
[HttpPost]
public ActionResult Create(Learn learn)
{
if (ModelState.IsValid)
{
db.Learns.Add(learn);
db.SaveChanges();
return RedirectToAction("Details", new { id = learn.ModuleId });
}
ViewBag.ModuleId = new SelectList(db.Modules, "ModuleId", "Code", learn.ModuleId);
return View(learn);
}
Here is the detail in my LearnController
public ViewResult Details(int id)
{
var mod = db.Modules.Include("Learns").Single(g => g.ModuleId == id);
return View(mod);
}
In my create view of the learning controller, I place a HTMLActionLink but I can't seem to be redirected. Is there something wrong with my codes?
#Html.ActionLink("Back to List", "Details")
This is the error.
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ViewResult Details(Int32)' in 'Module1.Controllers.LearnController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
The link would need to be #Html.ActionLink("Back to List", "Details", new { id = # }) where # is the id of the Module to display. So when you create a new Learn the GET method would need to be something like
public ActionResult Create(int ID) // ID is the ModuleId your creating the Learn for
{
Learn model = new Learn();
model.ModuleID = ID;
return View(model);
}
and in the view
#model Learn
....
#Html.ActionLink("Back to List", "Details", new { id = Model.ModuleID })
This is assuming that the Details view contains a link to create a new Learn for the Module, where the link would be #Html.ActionLink("Add new Learn", "Create", new { id = Model.ID})
If you can create a new Learn from some other view (where you don't have access to the ID property of Module), and where the view to create a new Learn allows you to select the Module form a drop down list, then you would need javascript/jquery to update the href value of the link based on the selected Module
"The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32", the parameters should be nullable type, change to this
public ViewResult Details(int? id)
{
try
{
if (id.HasValue)
{
var mod = db.Modules.Include("Learns").Single(g => g.ModuleId == id);
if (mod == null)
throw new ArgumentException("Invalid ID");
return View(mod);
}
else
{
throw new ArgumentException("Invalid ID");
}
}
catch (ArgumentException ex)
{
return View(new Modules()); // assumed Details view model is Modules class
}
catch
{
return View(new Modules()); // assumed Details view model is Modules class
}
}
or
public ViewResult Details(string id)
{
try
{
int intId;
if(int.TryParse(id, out intId))
{
var mod = db.Modules.Include("Learns").Single(g => g.ModuleId == intId);
if (mod == null) throw new ArgumentException("Invalid ID");
return View(mod);
}
else
{
throw new ArgumentException("Invalid ID");
}
}
catch (ArgumentException ex)
{
return View(new Modules()); // assumed Details view model is Modules class
}
catch
{
return View(new Modules()); // assumed Details view model is Modules class
}
}
Update
I think I misunderstood your question, all you need to do is change
#Html.ActionLink("Back to List", "Details" })
to
#Html.ActionLink("Back to List", "Details", new { id = Model.ModuleId })
as suggested by Stephen Muecke

how could i change these code to MVC3 Razor

how could i change these code to MVC3 Razor,it script code.
these code i get form http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-5-master-page-templates.html
it's MVC2 Template and i want change it to Razor.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script runat="server">
private object ModelValue {
get {
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelMetadata.Model) {
return String.Format(
System.Globalization.CultureInfo.CurrentCulture,
"{0:0.00}", ViewData.ModelMetadata.Model
);
}
return ViewData.TemplateInfo.FormattedModelValue;
}
}
</script>
<%= Html.TextBox("", ModelValue, new { #class = "text-box single-line" }) %>
i changed to Razor as bellow but it can't work:
#{
private object FormattedValue
{
get
{
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelMetadata.Model)
{
return String.Format(System.Globalization.CultureInfo.CurrentCulture,"{0:0.00}",ViewData.ModelMetadata.Model);
}
return ViewData.TemplateInfo.FormattedModelValue;
}
}
}
#Html.Encode(FormattedValue)
Create your own function:
http://weblogs.asp.net/hajan/archive/2011/02/05/functions-inside-page-using-razor-view-engine-asp-net-mvc.aspx
just call the function passing it the value in, and return it however you want to format it.
#functions{
public MvcString FormatValue(object valuetoFormat)
{
...logic here ...
return ....
}
}
Calling it is roughly:
#Html.TextBox("", FormatValue(ModelValue), new { #class = "text-box single-line" })
I don't think you can specify adhoc properties in Razor. You can, however, create variables.
#{
object FormattedValue;
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelMetadata.Model)
{
FormattedValue = String.Format(System.Globalization.CultureInfo.CurrentCulture,"{0:0.00}",ViewData.ModelMetadata.Model);
}else{
FormattedValue = ViewData.TemplateInfo.FormattedModelValue;
}
}
Hope this works for you?
If you use the #{ } tag, the code is inserted inside the method used to generate the output.
You should use #functions { } to define elements you want on class (=page) level.
This would make your code look like:
Read SLaks blog for more information.
#functions {
private object FormattedValue
{
get
{
if (ViewData.TemplateInfo.FormattedModelValue == ViewData.ModelMetadata.Model)
{
return String.Format(System.Globalization.CultureInfo.CurrentCulture,"{0:0.00}",ViewData.ModelMetadata.Model);
}
return ViewData.TemplateInfo.FormattedModelValue;
}
}
}
#Html.Encode(FormattedValue)

Storing ActionLink in model or RouteValueDictionary in model

I wish to store an action link in the model.
Something like
public MvcHtmlString ActionLink_New
{
get { return Html.ActionLink("new", "Edit", "News", new { Area = "Admin" }, null); }
}
It appears the model needs a webviewpage context.
Failing that, I thought I would store just the route values.
public RouteValueDictionary[] RouteValue_New
{
get { return new RouteValueDictionary[] { Area = "Admin" }; }
}
//View
#Html.ActionLink("new", "Edit", "News", Model.RouteValue_New, null)
The Area in the property is red. Is either or both scenario achievable. What do i need to add to get this to work, thanks.
try this
public object RouteValue_New
{
get {
return new { Area = "Admin" };
}
}

Orchard CMS rendering parts outside of admin section

I am working on a module by following the instructions here http://orchardproject.net/docs/Creating-a-module-with-a-simple-text-editor.ashx
The one change I want to do is, rendering the product creation outside of admin module. So I created homecontroller like this
public class HomeController : Controller
{
public HomeController(IContentManager cm) {
ContentManager = cm;
}
private IContentManager ContentManager { get; set; }
public ActionResult Index() {
return Content("This is index");
} [Themed]
public ActionResult Create()
{
var product = ContentManager.New("Product");
var model = ContentManager.BuildEditor(product);
return View((object) model);
}
and a file routes.cs in the root folder
public class Routes : IRouteProvider
{
public void GetRoutes(ICollection<RouteDescriptor> routes)
{
foreach (var routeDescriptor in GetRoutes())
routes.Add(routeDescriptor);
}
public IEnumerable<RouteDescriptor> GetRoutes()
{
return new[] {
new RouteDescriptor {
Priority = 5,
Route = new Route(
"commerce",
new RouteValueDictionary {
{"area", "SimpleCommerce"},
{"controller", "Home"},
{"action", "Index"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "SimpleCommerce"}
},
new MvcRouteHandler())
},
new RouteDescriptor {
Priority = 6,
Route = new Route(
"commerce/Create",
new RouteValueDictionary {
{"area", "SimpleCommerce"},
{"controller", "Home"},
{"action", "Create"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "SimpleCommerce"}
},
new MvcRouteHandler())
}
};
}
}
So how should I move from here onwards to render this whole thing together when I navigate to url http://localhost:35713/commerce/create
But it throws an error saying create view didnt find. Then I created a view (create.cshtml) in Views/Home folder
#model SimpleCommerce.Models.ProductPart
<fieldset>
<label class="sub" for="Sku">#T("Sku")</label><br />
#Html.TextBoxFor(m => m.Sku, new { #class = "text" })<br />
<label class="sub" for="Price">#T("Price")</label><br />
#Html.TextBoxFor(m => m.Price, new { #class = "text" })
</fieldset>
Now it throws an error saying
The model item passed into the dictionary is of type 'IShapeProxyedcfa08c61cf49898dc2e77bde025039', but this dictionary requires a model item of type 'SimpleCommerce.Models.ProductPart'.
Oh, that's cross-posted. BuildEditor is creating a shape, and your template is expecting a strongly-typed model. Dropping the #Model directive should substitute that problem with another (which is that you won't be able to use the Lambda-based helpers with shapes.

Resources