I have a requirement to change the content of a .Net core mvc web app based on culture[Lauguage] and a particular session value which will be set based on a dropdown selection.
I have a dropdown of States [PA, VA, .. etc], so created resource files viewname.PA.en.resx, viewname.VA.en.resx.
Need to load the appropriate resource file based on my dropdown selection and language culture.
currently it's loading based language culture only.
Please refer the official document and sample.
You could create a _SelectLanguagePartial.cshtml partial view with the following code:
#using Microsoft.AspNetCore.Builder
#using Microsoft.AspNetCore.Http.Features
#using Microsoft.AspNetCore.Localization
#using Microsoft.AspNetCore.Mvc.Localization
#using Microsoft.Extensions.Options
#inject IViewLocalizer Localizer
#inject IOptions<RequestLocalizationOptions> LocOptions
#{
var requestCulture = Context.Features.Get<IRequestCultureFeature>();
var cultureItems = LocOptions.Value.SupportedUICultures
.Select(c => new SelectListItem { Value = c.Name, Text = c.DisplayName })
.ToList();
var returnUrl = string.IsNullOrEmpty(Context.Request.Path) ? "~/" : $"~{Context.Request.Path.Value}";
}
<div title="#Localizer["Request culture provider:"] #requestCulture?.Provider?.GetType().Name">
<form id="selectLanguage" asp-controller="Home"
asp-action="SetLanguage" asp-route-returnUrl="#returnUrl"
method="post" class="form-horizontal" role="form">
<label asp-for="#requestCulture.RequestCulture.UICulture.Name">#Localizer["Language:"]</label> <select name="culture"
onchange="this.form.submit();"
asp-for="#requestCulture.RequestCulture.UICulture.Name" asp-items="cultureItems">
</select>
</form>
</div>
Then, add the Views/Shared/_SelectLanguagePartial.cshtml file is added to the footer section of the layout file so it will be available to all views:
<div class="container body-content" style="margin-top:60px">
#RenderBody()
<hr>
<footer>
<div class="row">
<div class="col-md-6">
<p>© #System.DateTime.Now.Year - Localization</p>
</div>
<div class="col-md-6 text-right">
#await Html.PartialAsync("_SelectLanguagePartial")
</div>
</div>
</footer>
</div>
Then, in Home Controller SetLanguage method, set the culture cookie.
[HttpPost]
public IActionResult SetLanguage(string culture, string returnUrl)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) }
);
return LocalRedirect(returnUrl);
}
[Note] You can't plug in the _SelectLanguagePartial.cshtml to sample code for this project. The Localization.StarterWeb project on GitHub has code to flow the RequestLocalizationOptions to a Razor partial through the Dependency Injection container. Check the Startup.cs.
Related
I'm trying to build an ASP.NET Core 6 MVC app.
I have two controllers 'home' and 'work'. How can I display data from 'work' model in the home/index view?
Unfortunately, I don't know the terminology to better formulate the question, but actually I want to achieve that on the home page I can display the last few works.
The part where I enter/edit data works fine. When I go to the /Works/Index page, I see all the entered data. How to display, for example, the last 4 on the home page of the application
In index.cshtml I have
#model IEnumerable<Website.Models.Work>
#foreach (var item in Model)
{
<div class="col-lg-3 col-sm-6">
<div class="gallery-item wow fadeInUp delay-0-2s">
<img src="~/assets/images/gallery/gallery-1.jpg" alt="Gallery">
<div class="gallery-content">
<span class="category">#Html.DisplayFor(modelItem => item.Tag)</span>
<h5>#Html.DisplayFor(modelItem => item.Headline)</h5>
</div>
</div>
</div>
}
But I get an error:
System.NullReferenceException: Object reference not set to an instance of an object.
If you just want to know how to display the data on page, here is a working demo below:
HomeController
public class HomeController: Controller
{
private readonly YourDbContext _context;
public HomeController(YourDbContext context)
{
_context = context;
}
public IActionResult Index()
{
//get the last 4 records...
var model = _context.Work.AsEnumerable().Reverse().Take(4).ToList();
return View(model);
}
}
Home/Index.cshtml
#model IEnumerable<Website.Models.Work>
#foreach (var item in Model)
{
<div class="col-lg-3 col-sm-6">
<div class="gallery-item wow fadeInUp delay-0-2s">
<img src="~/assets/images/gallery/gallery-1.jpg" alt="Gallery">
<div class="gallery-content">
<span class="category">#Html.DisplayFor(modelItem => item.Tag)</span>
<h5>#Html.DisplayFor(modelItem => item.Headline)</h5>
</div>
</div>
</div>
}
I am still trying to get to grips with Razor Pages for Net Core and seem to be a bit stuck on this. I have my Index.cshtml:
#page
#model IndexModel
<input type="hidden" name="hdnPageSelector" id="hdnIndexPage" />
<div class="text-center">
<p>Welcome to</p>
<h1 class="display-4">"My Web App"</h1>
</div>
<div class="form-row">
<div class="form-group col-md-2">
<partial name="IndexPartials/_Navigation" />
</div>
<div class="form-group col-md-1">
</div>
<div class="form-group col-md-6">
<partial name="IndexPartials/_Body" />
</div>
<div class="form-group col-md-1">
</div>
<div id="refreshMembers" class="form-group col-md-2">
<partial name="IndexPartials/_Members" />
</div>
</div>
Note the last div has an id="refreshMembers".
The partial view (_Members) that is loaded there looks like this:
#model IndexModel
<label>Members</label>
<br />
#{
foreach (ApplicationUser user in Model.AppUsersList)
{
if (user.IsLoggedIn)
{
<label>#user.FirstName #user.LastName </label>
<span class="dot"></span>
}
else
{
<label>#user.FirstName #user.LastName</label>
}
}
}
Within the controller I have a property called:
public IList<ApplicationUser> AppUsersList { get; set; }
And this is populated on OnGetAsync() as follows:
AppUsersList = _userManager.Users.OrderBy(x => x.FirstName).Where(y => y.UserName != currentUser.UserName).ToList();
This is fine, the page loads with the partial view populated as expected. I now want the partial to refresh every 5 seconds so I have put this piece of Javascript/JQuery in place:
$(function () {
setInterval(function () {
$("#refreshMembers").load("/Index?handler=RefreshMembers");
}, 5000);
});
with the following method setup:
public async Task<IActionResult> OnGetRefreshMembers()
{
var currentUser = await _userManager.GetUserAsync(User);
AppUsersList = _userManager.Users.OrderBy(x => x.FirstName).Where(y => y.UserName != currentUser.UserName).ToList();
return new PartialViewResult
{
ViewName = "_Members",
ViewData = new ViewDataDictionary<List<ApplicationUser>>(ViewData, AppUsersList)
};
}
However the partial view doesn't get refreshed. If I put a breakpoint within this method I can see it is being hit every 5 seconds, despite Devtools stating there is an error on each attempt:
In a nut shell, I just can't seem to get my partial view to be reloaded every 5 seconds. It feels like I am close but just missing something and don't know what that is.
Having been reminded to check the Output window in VS a bit better, I found the cause of my problems... Well two things actually. This is the corrected method:
public async Task<IActionResult> OnGetRefreshMembers()
{
var currentUser = await _userManager.GetUserAsync(User);
AppUsersList = _userManager.Users.OrderBy(x => x.FirstName).Where(y => y.UserName != currentUser.UserName).ToList();
return new PartialViewResult
{
ViewName = "IndexPartials/_Members",
ViewData = new ViewDataDictionary<IndexModel>(ViewData, this)
};
}
Where...
I didn't include the folder that the partial lives in when naming it on the PartialViewResult
I need to return the entire IndexModel object - having updated the AppUserList property, and not just the list of AppUsers.
Within a view I am trying to load a partial view from the controller. I am using the Ajax.ActionLink method to make this call
#Ajax.ActionLink("Involved Entities/Resources",
"GetNarratives",
new { id = 4 },
new AjaxOptions { HttpMethod = "GET",
UpdateTargetId = "narrContainer" })
Further down in the page I have a div element with the id of narrContainer
<div id="narratives" class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"><a data-toggle="collapse" href="#collapseNarrative">Narratives [ #Model.AssociatedNarrative.Count() ]</a></h4>
</div>
<div id="collapseNarrative" class="panel-collapse collapse">
<div class="panel-body">
<div id="narrContainer"></div>
</div>
</div>
</div>
</div>
</div>
The controller has the following code:
public PartialViewResult GetNFIRNarratives(string id)
{
//Get Narratives
fauxModel fm = new fauxtModel();
List<Narrative> narr = new List<Narrative>();
narr = fm.GenerateMockBaseNarratives(4);
return PartialView("_myAssociatedNarrative", narr);
}
The partial view contains fields for the collection:
<!-- Assocaited Narrative-->
#for (int i = 0; i < #Model.Count(); i++)
{
<div class="col-md-12">
<p><strong>Date Entered</strong> #Html.DisplayFor(x => x[i].DateEntered)</p>
</div>
if (!string.IsNullOrWhiteSpace(#Model[i].Title))
{
<div class="col-md-12">
<p><strong>Narrative Title</strong> #Html.DisplayFor(x => x[i].Title) </p>
</div>
}
<div class="col-md-12">
<p>#Html.DisplayFor(x => x[i].NarrativeText)</p>
<hr />
</div>
}
Within the base layout page I have added reference to the unobtrusive-ajax script as
<script src="#Url.Content("~/js/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
And I have confirmed that the key is enabled in the web.config file.
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
When I click on the link to load the narratives the code executes but it does not load the partial in the div element. Instead it is replacing the current view the partial. What I am missing that is causing the partial to be loaded /replacing the current document and what do I need to change to get the partial to render only within the specified div element?
After working through the Post Event more I discovered that the #Ajax.Action was not posting as an AJAX call. This was the result of the jquery-unobtrusive-ajax.js file being outdated and containing deprecated jquery functions (.live()) .
I updated the files through NuGet using
Install-Package Microsoft.jQuery.Unobtrusive.Ajax
and the actions are now working correctly.
I have a partial view like this (simplified):
#model Portal.Models.LoginModel
<div class="login-container k-block">
<section id="login-form" class="">
#using (Html.BeginForm(actionName, controllerName, new { ReturnUrl = ViewBag.ReturnUrl }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset id="login-form-list-items">
<ol>
<li>
#Html.LabelFor(m => m.CardNumber)
#Html.TextBoxFor(m => m.CardNumber, new { #class="k-textbox"})
<div class="k-error-colored">
#Html.ValidationMessageFor(m => m.CardNumber)
</div>
</li>
<li>
#Html.LabelFor(m => m.Pin)
#Html.PasswordFor(m => m.Pin, new { #class="k-textbox"})
<div class="k-error-colored">
#Html.ValidationMessageFor(m => m.Pin)
</div>
</li>
<input id="login-input-submit" class="k-button" type="submit" value="Enter" />
</fieldset>
</div>
And in my login view I call this partial view like:
#model Portal.Models.LoginModel
#Html.Partial("_LoginFormNoRegistration", Model, new ViewDataDictionary { { "actionName", "Login" }, { "controllerName", "Account" } })
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
The problem is that when the login method in the controller adds an error like:
public ActionResult Login(LoginModel model, string returnUrl)
{
//...
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
The message is not show in the validation summary... I don't understand why... What could be the problem? Some javascript library missing?
Update
I also found that the form generated as the novalidate attribute set:
<form action="/" method="post" novalidate="novalidate">
//...
</form>
I don't know why.
I found the problem.
I was passing a new ViewData in the RenderPartial which was overriding the ViewData of the parent view, so the model state was lost, as explained here: Pass Additional ViewData to an ASP.NET MVC 4 Partial View While Propagating ModelState Errors.
Changing the main view to:
#model Portal.Models.LoginModel
#{
ViewData.Add("actionName", "Login");
ViewData.Add("controllerName", "Account");
Html.RenderPartial("_LoginFormNoRegistration", Model, ViewData);
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Did the trick!
Also, if you want to show a general error message for the model in the validationsummary, be sure to add the error with an empty string as key:
ModelState.AddModelError("error", "The user name or password provided is incorrect."); - doesn't work
ModelState.AddModelError("", "The user name or password provided is incorrect."); - works
Remove the true argument in #Html.ValidationSummary()
It could be a few different things off the top of my head. First off you may not be including the required JavaScript. You may not need all of these but i include these in almost all of my layout views.
<script src="#Url.Content("~/Scripts/jquery-1.8.3.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
Also, could you show the code for your partial view? If you are referencing values that are inside a child class in your model the validation can act a little wonky sometimes.
Lastly, this sounds silly but as you did not post the code for your login model make sure you are using the proper data annotations for the values that you want the validation to show up for.
I'm trying to filter a list of entities and update the partial view on the page with the filtered data. The partial view is returning the correct model with the filtered data, but is not being rendered inside the parent page. Instead it is being rendered in "body" element of an empty HTML page. I've found many topics on this but even though I appear to be following their directions, I'm still having no luck. A fresh set of eyes from the community here may be a huge help.
#model PennLighting.ViewModels.LinecardViewModel
#{
ViewBag.Title = "Linecard";
}
<div class="linecard-head">
#using (Ajax.BeginForm("Index",
new AjaxOptions
{
UpdateTargetId = "linecard"
}))
{
#Html.EditorFor(model => model.Categories)
<div class="buttons">
<input type="submit" name="btnFilter" value="Filter" />
<input type="submit" name="btnShowAll" value="Show All" />
</div>
}
</div>
<div id="linecard">
#Html.Partial("Linecard")
</div>
#section Scripts
{
#Scripts.Render("~/bundles/jqueryval")
}
public ActionResult Index()
{
var viewModel = new LinecardViewModel();
viewModel.Categories = db.Categories
.OrderBy(c => c.Name).ToList();
viewModel.Manufacturers = db.Manufacturers
.OrderBy(m => m.Name).ToList();
return View(viewModel);
}
public ActionResult Index(string btnFilter, string[] selectedCategories)
{
var viewModel = new LinecardViewModel();
var selectedMfrs = new List<Manufacturer>();
if (btnFilter != null && selectedCategories != null)
{
var categoryIds = selectedCategories.Select(c => int.Parse(c)).ToArray();
if (categoryIds != null)
{
selectedMfrs = db.Manufacturers
.Where(m => m.Categories.Any(c => categoryIds.Contains(c.ID)))
.OrderBy(m => m.Name).ToList();
}
}
else
selectedMfrs = db.Manufacturers.OrderBy(m => m.Name).ToList();
viewModel.Manufacturers = selectedMfrs;
return PartialView("Linecard", viewModel);
}
<!DOCTYPE html>
<html>
<head>
<title>#ViewBag.Title</title>
#Styles.Render("~/Content/themes/base/css", "~/Content/css")
</head>
<body>
<div id="container" class="round-bottom">
<div id="header">
<div id="header-left">
<div id="logo">
<a href="#Url.Content("~/")">
<img src="#Url.Content("~/Content/Images/logo.png")" alt="Penn Lighting Associates" /></a>
</div>
</div>
<div id="header-right">
<ul class="nav">
<li>#Html.ActionLink("Home", "Index", "Home")</li>
<li>#Html.ActionLink("About", "Index", "About")</li>
<li>#Html.ActionLink("Linecard", "Index", "Linecard")</li>
<li>#Html.ActionLink("Events", "Index", "Events")</li>
<li>#Html.ActionLink("Gallery", "Index", "Gallery")</li>
<li>#Html.ActionLink("Contact", "Index", "Contact")</li>
<li><a href="http://oasis.pennlighting.com:81/OASIS/desk/index.jsp" target="_blank">
Customer Login</a></li>
</ul>
</div>
</div>
<div id="main">
#RenderBody()
</div>
</div>
<div id="footer">
<p>
Copyright © 2008 Penn Lighting Associates</p>
</div>
#Scripts.Render("~/bundles/jquery")
#RenderSection("scripts",false)
</body>
</html>
So what am I missing? Thanks!
You cannot have 2 actions on the same controller with the same name accessible on the same HTTP verb. You might want to decorate your Index contorller action that is invoked with an AJAX call and returns a partial with the [HttpPost] attribute:
[HttpPost]
public ActionResult Index(string btnFilter, string[] selectedCategories)
{
...
}
Without seeing more of your solution, it's a bit fuzzy, but I believe you want to still return the Index and pass the model data into the Partial in your view. The way you are doing it would return only the partial view, which is why you're getting those results.
So in the filtered index:
return View(viewModel)
And in the index view, pass the data to the partial, which I assume without seeing has the right model association to display.
UPDATE
If you're looking to dynamically pull a subset of data and leave the rest untouched, then do an AJAX POST with the filter information to the action specified for the partial view. Take the data results and place them in the Linecard div.
There are many ways to send the data (bundle by JSON, serialize form, individual data points). Here are some examples:
http://brandonatkinson.blogspot.com/2011/01/using-jquery-and-aspnet-mvc-to-submit.html
MVC ajax json post to controller action method
The problem was that my jqueryval bundle was missing the jquery.unobtrusive-ajax.js file. My code works as is once that was included.