Set Cookie value when link is clicked in Razor Pages - asp.net-core

In a NET Core 7 using Razor Pages I have a HeaderViewComponent:
public class HeaderViewComponent : ViewComponent {
public HeaderViewComponent() { }
public IViewComponentResult Invoke() {
return View();
}
}
Where part of the HTML is:
<ul>
#foreach (String culture in cultures) {
<li>
<a href='#Url.RouteUrl(new RouteValueDictionary { { "culture", #culture } })'>#culture</a>
</li>
}
</ul>
I have this View Component in all my Razor Pages.
When one of the links is clicked I want more than redirect.
I also need to set the value of a cookie named "Culture".
What would be the best way to do this?

Related

ASP.NET Core - MVC - Model variable in view - null exception

In an attempt to learn ASP.NET Core MVC I've made a simple project and am trying to pass a model instance created in the controller to the view.
Controller Code - I create a simple list, then pass it to the view, being explicit about which view
public class TableController : Controller
{
public IActionResult Index()
{
var modelData = new List<string> {"A", "B"};
ViewBag.Title = "Tables";
return View("/Pages/Table.cshtml", modelData);
}
}
View Code
#page
#model List<string>
<div class="text-center">
<h1 class="display-4">#ViewBag.Title</h1>
#if (Model == null)
{
<p>There is no data to be displayed</p>
}
else
{
<ul>
#foreach (string str in Model)
{
<li>#str</li>
}
</ul>
}
</div>
When I set a breakpoint in the Controller the object I pass in as the model parameter is not null:
However, when I step through into the view code I get this:
I've looked at a few other "Model is null" posts but they were due mismatching types between whats passed in the View() model parameter and whats expected in the view given by the #model declaration.
It's probably something really simple but I'm not sure where I've gone wrong?
I had the same exception, the solution was to remove #page in Index.cshtml and then Boom, everything was there. Took me 5-6 hours to "resolve" this exception but better late, than never.
In asp.net MVC,View does not mean Razor Page.You can use a View page,And add a Table folder in Views.And then add a Index.cshtml(Razor View Page)to it.
Here is a demo worked:
Controller(return View() inIndex action will find a Index.cshtml in Views/Table(Views/ControllerName)):
public class TableController : Controller
{
public IActionResult Index()
{
var modelData = new List<string> { "A", "B" };
ViewBag.Title = "Tables";
return View(modelData);
}
}
View(Don't use #page,it's used in Razor Page):
#model List<string>
#{
ViewData["Title"] = "Table_Index";
}
<div class="text-center">
<h1 class="display-4">#ViewBag.Title</h1>
#if (Model == null)
{
<p>There is no data to be displayed</p>
}
else
{
<ul>
#foreach (string str in Model)
{
<li>#str</li>
}
</ul>
}
</div>
Views folder structure(Each folder means a controller except Shared):
Result:

Tag helpers not working with DynamicRouteValueTransformer

ASP.NET Core's tag helpers appear not to be working in combination with DynamicRouteValueTransformer.
Here's an MCVE: let's say we have a simple controller with two actions...
public class GenericController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Add()
{
return View();
}
}
... and the Index view links to the Add action with the asp-action tag helper:
<!DOCTYPE html>
<html>
<body>
<a asp-action="Add">Add new!</a>
</body>
</html>
When we now open /Generic in a browser and inspect the page, we'll notice that ASP.NET generated the expected href value for the controller action:
Add new!
So far, so good. Now let's create a DynamicRouteValueTransformer which routes all requests to that same controller...
public class Transformer : DynamicRouteValueTransformer
{
public override ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
{
if (values.ContainsKey("controller"))
{
values["originalController"] = values["controller"];
values["controller"] = "Generic";
}
return new ValueTask<RouteValueDictionary>(values);
}
}
... and let's set it up for endpoint routing in Startup.cs...
services.AddSingleton<Transformer>();
...
app.UseEndpoints(endpoints =>
{
endpoints.MapDynamicControllerRoute<Transformer>("{controller=Home}/{action=Index}/{id?}");
});
After opening /Generic (or any other route, e.g. /Foobar) in a browser and inspecting the page, we'll now notice that asp-action tag helper is no longer working:
Add new!
Since the href value is empty, the link is no longer working. It looks the dynamic routing broke the tag helper.
Any suggested fix or workaround?

Razor #page routing to a different page

I'm using aspnet core 3.
In a controller, my user can type in a single url like ... /login ... and then I test which company it is and return the correct view - like this:
[Route("login")]
public IActionResult Login()
{
return _company == "a" ? View("ThisView") : View("ThatView");
}
But if I use Razor routing ... #page "/login" ... how can I do the same? I need the user to type a single url, but then I need to test which company it is and then direct to the correct razor page.
You can use "RedirectToPageResult()" in razor page to return to
the corresponding page instead of view.
The premise is that you need to create the corresponding page to
return.
For example, I have a page named MyTest, user can input the parameter in this page.
If user enter "a" , then return to ThisPage.cshtml, otherwise, return to ThatPage.cshtml.
MyTest.cshtml:
#page
#model WebApplication1_rzaor_page.MyTestModel
#{
ViewData["Title"] = "MyTest";
Layout = "~/Pages/Shared/_Layout.cshtml";
}
<h1>MyTest</h1>
<form asp-page-handler="Login" method="post">
<input id="Text1" type="text" name="_company" />
<input id="Submit1" type="submit" value="submit" />
</form>
MyTest.cshtml.cs:
public class MyTestModel : PageModel
{
public void OnGet()
{
}
public IActionResult OnPostLogin(string _company)
{
return _company == "a" ? new RedirectToPageResult("ThisPage") : new RedirectToPageResult("ThatPage");
}
}
Here is the result :

ViewBags using #Html.Action to render form on partial view

View:
<p>Parent ViewData: #ViewData["Test"]</p>
#Html.Action("MemberSignup","MemberSignupSurface")
PartialView:
<p>PartialView ViewData: #ViewData["Test"]</p>
#using (Html.BeginUmbracoForm<MemberSignupSurfaceController>
("MemberSignupSubmit", "MemberSignupSurfaceController",FormMethod.Post))
{
<!-- Some form controls -->
<input type="submit" value="Signup" />
}
Controller:
public class MemberSignupSurfaceController : SurfaceController
{
public ActionResult MemberSignup()
{
ViewData["Test"] = "From MemberSignup";
// Do database stuff and create model from that
return PartialView("MemberSignupView", model);
}
[HttpPost]
public ActionResult MemberSignupSubmit(MemberViewModel model)
{
ViewData["Test"] = "From MemberSignupSubmit";
if (ModelState.IsValid)
{
// Redirect to success using TempData
}
else
{
return CurrentUmbracoPage();
}
}
}
When my page load MemberSignup is called and the page shows
Parent ViewData:
PartialView ViewData: From MemberSignup
Then when i submit the form on the partial view with invalid input so it won't validate and it calls CurrentUmbracoPage() in the action MemberSignupSubmit
I get the following:
Parent ViewData: From MemberSignupSubmit
PartialView ViewData: From MemberSignup
If i use #Html.Partial to render my partial view both viewbags shows the same value set from the submit action.
I've tried TempDatabut it is not working either. Is there really no way to pass anything back to the partial view after i return from the submit action when using #Html.Action to render a partial view form.
The overall problem I am trying to solve is to popluate a dropdown in my form with values from the database. Using #Html.Partial don't allow me to do this but have a working viewbag.
I did this to render a dynamic dropdown list with values from a database. Maybe it will help someone.
It is a music player which needs a dynamic db populated menu to list the playlists
I made a base controller which all other controllers inherit from. In that base class, I have a PlaylistPopupMenu action which gets the list of playlists from a db.
public PartialViewResult PlaylistPopupMenu()
{
try
{
return PartialView("_PlaylistPopupMenu", db.GetPlaylists(1).ToList());
}
catch (Exception)
{
throw;
}
}
Then I have a _PlaylistPopupMenu partial view as follows:
#model List<OneMusic.Models.GetPlaylists_Result>
#if (Model.Count > 0)
{
<li style="height:2px" class="divider"></li>
foreach (var item in Model)
{
<li style="height:30px">#Html.DisplayFor(p => item.Name)
#Html.ActionLink(item.Name, "AddSong", "Playlist", new { playlistId = #item.PlaylistId, songId = 1 }, "")
</li>
}
}
this renders the dynamic parts of the menu (ie the playlists)
Finally the main page has this to build the dynamic part of the menu:
<ul class="dropdown-menu" style="margin-top:10px"><p class="text-primary" style="margin-left:18px; margin-top:6px">Add To</p>
<!-- other static menu items here-->
<li style="margin-top:-60px; height:0px">#Html.Action("PlaylistPopupMenu")</li>
</ul>

Gracefully handling permalinks when using pushstate for partial view pagination

I'm looking for a clean way to handle permalinks when my application uses pushstate to load ASP.NET MVC partial views for pagination
If my controller returns a partial view (currently) then the permalink will display the partial view alone without the rest of the site defined by _Layout.cshtml.
If my controller return a regular view, the permalink works but loading the entire page into the container provided only for part of the page creates a type of Droste Effect.
The only solution I've found is to redirect the 404 to the homepage and navigate to correct page using the relevant portion of the url.
However, this prevents me from using my custom error page (which just happens to be an awesome error page).
Ok I figured out how to make this work with a double-controller setup.
NavigationController handles ajax calls and returns partial views (note there is no "Index")
public class NavigationController : Controller
{
public PartialViewResult Home()
{
return PartialView("Home", null);
}
public PartialViewResult About()
{
return PartialView("About", null);
}
...
}
Separate PermalinkController handles permalinks by returning entire regular Views (note this has an "Index" but no "Home":
public class PermalinkController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
...
}
Routes:
routes.MapRoute(
"PermalinkRouter",
"{action}",
new { controller = "Permalink", action = "Index" },
new { }
);
routes.MapRoute(
"NavigationRouter",
"Navigation/{action}/{id}",
new { controller = "Navigation", action = "Home", id = UrlParameter.Optional },
new { }
);
Views:
~/Views/Shared/_Layout.cshtml:
...
<header></header>
<div id="pageViewContainer">
#RenderBody()
</div>
<footer></footer>
...
~/Views/Navigation/Home.cshtml PartialView:
<div> Home Content </div>
~/Views/Permalink/Index.cshtml View:
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#Html.Partial("~/Views/Navigation/Home.cshtml", null)