I have a view that has multiple partial views on it. It is a settings page. They can click a site on the left panel, ad it will display all the settings for managing that site in the partial view on the page. When I build the links of the sites, I use the following Razor code as an example:
List<Site> siteList = Site.GetSites(new SiteQuery());
foreach (Site site in siteList)
{
<li>#Html.ActionLink(#site.Name, "SelectSite", "SettingsController", new { id = Model.EnvironmentID, siteId = #site.SiteID}, null)</li>
}
And here is the ActionResult I am trying to hit in the SetitngsController controller:
public ActionResult SelectSite(int id, int siteId)
{
//..code here
}
The error occurs thhough, because my URL it outputs is as follows:
~/SettingsController/SelectSite/1?siteId=1
instead of being ~/SettingsController/SelectSite/?id=1&siteId=1344
Am I obvioulsy misunderstanding how to be able to update the current view on the page with a new url and adding params to it this way. Is this wrong in MVC theory, or am I just missing somthing? Thanks in advance!
It is because of your routing table. By default it looks like {Controller}\{Action}\{id}, so your id param is right after action.
You can either change param name or change routing (but the last solution has much more complexity), for example:
#Html.ActionLink(site.Name,
"SelectSite",
"SettingsController",
new { environmentID = Model.EnvironmentID, siteId = site.SiteID}, null)
public ActionResult SelectSite(int environmentID, int siteId)
{
//..code here
}
Related
I am a beginner and I am going through some tutorials in my MVC. So, I came across two scenarios.
Scenario 1.
I had to pass some data to my view on post and then send that data as hidden field. Here is the code.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ForgotPassword(ForgotPasswordMV viewModel)
{
if (ModelState.IsValid)
{
return RedirectToAction("VerifyToken", new { emailId = viewModel.EmailId });
}
^^ USING ANONYMOUS OBJECTS
return View();
}
public ActionResult VerifyToken(string emailId = null)
{
VerifyTokenMV viewModel = new VerifyTokenMV
{
EmailId = emailId
};
return View(viewModel);
}
VerifyToken View
#using (#Html.BeginForm("VerifyToken", "Security"))
{
#Html.HiddenFor(m => m.EmailId)
<button class="btn btn-primary">Continue</button>
}
Works Perfectly fine. I am able to receive values of EmailId. So far so good.
Scenario 2.
Needed to open a partial view from Main view, here is the snippet.
Main cshtml file
<div class="abc">
#Html.Partial("../Widget/Customize", Model.Unit, new ViewDataDictionary() { { "ElementName", "UnitWidget" } })
</div>
partial cshtml file
#{
string custWidgetElementName = ViewBag.ElementName;
}
// some html code below
Observation:
In scenario 2 why have I used ViewDataDictionary. Although both example works perfectly fine. But is there any reason that I had to use ViewDataDictionary. In scenraio 1 can we use ViewDataDictionary? If Yes, then which one is optimum solution.
Question: When I need to pass values shall I use new {key : value} or use ViewDataDictionary or there is no corelation? Instead of ViewDataDictionary can I use anonymous object in Senario 2
Your two scenarios are totally different. They are not doing the same thing.
In scenario 1 when using this line:
return RedirectToAction("VerifyToken", new { emailId = viewModel.EmailId });
A new URL is genrated and sent back to the client (the browser) with HTTP Code 301 or 302. When received the browser will re-contact your application wiht the received URL. With that URL, your application will execute the associated action. In your case, the client's browser will call VerifyToken action with the emailId parameter setted when you call RedirectionToAction into ForgotPassword action. So using RedirectionToAction method is just telling that method to generate a new URL with parameter defined in the anonymous type.
In scenario 2 is completely different to scenario 1. With this line:
#Html.Partial("../Widget/Customize", Model.Unit, new ViewDataDictionary() { { "ElementName", "UnitWidget" } })
You're telling your view to inject the partial view which path is ../Widget/Customize. Because that partial view the strongly typed, you passed Model.Unit as an second parameter. You use also a third parameter new ViewDataDictionary() { { "ElementName", "UnitWidget" } } because your partial seems to internally need to access to the dynamic property ViewBag or dictionary property ViewData of your view.
Conclusion:
In scenario 1 you are just telling the client's browser to go to the new URL you have generated after requesting ForgetPassword URL. We just call that a rediretion.
In scenario 2, you're just rendering a partial view into a view. The client's broswer doesn't know anything what's going on with partial views they don't know if they exist.
Here is the hyperlinked code I am generating to show a list of merchants:
<td>
#Html.ActionLink(Html.DisplayFor(modelItem => item.MerchantName).ToHtmlString(), "ViewMerchant", "Merchants")
</td>
What I would like for it to do is to take me to a new page where it shows just the information on the merchant selected (address, webaddress, etc), so a ViewMerchant page in my Merchants View folder.
Can I grab the MerchantID in this ActionLink? If so how would that code look in the above ActionLink?
Secondly in the View Merchants page if anyone could link me to a site that would explain how to build that so the page gets populated with the merchant info would be ideal.
You can pass the MerchantID as a route value as follows:
#Html.ActionLink("Link text", "ViewMerchant", "Merchants", new { id = item.MerchantID }, null )
Where ViewMerchant is the name of your action instide MerchantsController
And here is a small sample to your details action:
public ActionResult Details(int id)
{
Merchant merchant = db.Merchants.Find(id);
return View(merchant);
}
Of course, you should use ViewModels instead of passing your model to the view. But that's another matter on which you can find many information online. Here is one link to start reading about it.
I'm using this MVCDonutCaching Nuget package because in the tutorial they said this was possible with child actions.
This question didn't work for me or I didn't understand it correctly.
If someone knows how to delete the child cache with the standard OutputCache attribute, then that is alright as well!
I've searched for this but I can't find it. See here an example:
Index action of the HomeController (homepage):
[AllowAnonymous]
public ActionResult Index()
{
return View();
}
ChildAction of the NewsController:
[AllowAnonymous]
[ChildActionOnly]
[DonutOutputCache(Duration = 600, Location = OutputCacheLocation.Server)]
public PartialViewResult LastArticles(int numberOfArticles)
{
return PartialView("_LastArticles", db.NewsArticles
.Include(i => i.Tags)
.Include(i => i.SeoTags)
.Where(n => n.Status == PublishStatus.Published)
.OrderByDescending(n => n.PublishDate)
.Take(numberOfArticles)
.ToList());
}
Index view of the HomeController:
#{ Html.RenderAction("LastArticles", "News", new { numberOfArticles = 2 }); }
To clear the cache I have an Admin area in my application with a controller and action in it to update the data stored by the child action. So when a news article is updated. The cache should get refreshed on the homepage.
In that action I have the following code:
var cacheManager = new OutputCacheManager();
cacheManager.RemoveItem("News", "LastArticles", new { area = "", numberOfArticles = 2 });
cacheManager.RemoveItem("News", "LastArticles", new { area = "" });
I tried multiple versions but without luck. Can anybody help me?
I believe you shouldn't explicitly define the (empty) area. Although the area's an important part of the routing, MVCDonutCaching does something weird in defining an internal key. Perhaps MVCDonutCache has a bug concerning area's.
I want to change:
www.testurl.com/sports/blog/1
Where sports is my area, blog is my action and 1 is an ID of a blog post, to:
www.testurl.com/sports/blog/test-title-of-blog
Where blog is still my action but the id is not shown, but instead the title/permalink of the blog is.
Here is my AreaRegistration for this action:
context.MapRoute(
"sports",
"sports/{action}/{content}",
new { area = "Sports", controller = "Sports", action = "", content = "" });
Here is my action at the moment:
[HttpGet]
public ActionResult Blog(string content)
{
int contentId;
if (Int32.TryParse(content, out contentId))
{
model = service.GetBlogById(contentId);
}
else
{
model = service.GetBlogByTitle(content);
}
//Change URL to be: www.testurl.com/sports/blog/ + model.SEOFriendlyTitle
return View(model);
}
Users are able to search via the ID of the blog, but also by the title of it, but I only want the title to appear in the url bar, never the id.
I cannot do this via Redirect rules due to the continuing maintenance that would cause.
Is the controller the right place to do this? -Remember I may not have my title until after I retrieve it from the database using the ID
How would I go about changing the URL to display the title vs. the ID?
I think what you should do is return a RedirectResult to the new Url if the ID is numeric and is a valid contentId :
int contentId;
if (Int32.TryParse(content, out contentId))
{
model = service.GetBlogById(contentId);
if(model != null)
{
return RedirectResult(/*url using the title*/);
}
}
else
{
model = service.GetBlogByTitle(content);
}
//Change URL to be: www.testurl.com/sports/blog/ + model.SEOFriendlyTitle
return View(model);
Of course, that will cause another round trip to the server but I can see a way to change the browser URL without a page redirect. You should also make sure that all published urls on your site are using the title instead of Id.
I hope it will help.
I suggest giving this a quick read.
http://www.dominicpettifer.co.uk/Blog/34/asp-net-mvc-and-clean-seo-friendly-urls
If you are really can't have the ID in Url and don't want to do redirects then I think storing Url as Slugs in the database is the only other option.
*Some points if you are going to do this.*
Add a Unique Constraint to the column at the Database Level to avoid duplicates.
Create a Database Index on this column to speed up you reads.
So with this Url
www.testurl.com/sports/blog/test-title-of-blog
This is your unique slug that you will query the database for instead of an ID
test-title-of-blog
I'm having some trouble with ASP.NET MVC Beta, and the idea of making routes, controller actions, parameters on those controller actions and Html.ActionLinks all work together. I have an application that I'm working on where I have a model object called a Plot, and a corresponding PlotController. When a user creates a new Plot object, a URL friendly name gets generated (i.e.). I would then like to generate a "List" of the Plots that belong to the user, each of which would be a link that would navigate the user to a view of the details of that Plot. I want the URL for that link to look something like this: http://myapp.com/plot/my-plot-name. I've attempted to make that happen with the code below, but it doesn't seem to be working, and I can't seem to find any good samples that show how to make all of this work together.
My Route definition:
routes.MapRoute( "PlotByName", "plot/{name}", new { controller = "Plot", action = "ViewDetails" } );
My ControllerAction:
[Authorize]
public ActionResult ViewDetails( string plotName )
{
ViewData["SelectedPlot"] = from p in CurrentUser.Plots where p.UrlFriendlyName == plotName select p;
return View();
}
As for the ActionLink, I'm not really sure what that would look like to generate the appropriate URL.
Any assistance would be greatly appreciated.
The answer is pretty simple: You have to supply enough values in your "ActionLink" that will fulfill your Route. Example:
<%= Html.ActionLink("Click Here", "ViewDetails", "Plot", new { name="my-plot-name" }, null)%>
If you leave out the "name=" part of the ActionLink method, then the RouteEngine won't see this link as being good enough to "match"... so then it would go to the default route.
This code above will make the URL look the way you want it.
How about this code-fix? (Note the name = null, appened to the end of the 4th line....)
routes.MapRoute(
"PlotByName",
"plot/{name}",
new { controller = "Plot", action = "ViewDetails", name = null }
);
and this should be renamed.. (notice plotName is renamed to name)
public ActionResult ViewDetails(string name ) { ... }
does that help?