How to apply CSS to Html.ValidationSummary at runtime - asp.net-mvc-4

I am building my first MVC4 website and I would like to show success message when page successfully submitted. I have achieved by using ModelState.AddModelError(("", "Data successfully saved."); but it is showing in the red color. I want to apply different css at runtime based on some conditions.
Thanks.

I recommend using TempData instead of changing validationsummary and #von described it very well. Use bootstrap. You could do something like this:
Controller
[HttpPost]
public ActionResult ManageUsers(UserViewModel model)
{
if (ModelState.IsValid)
{
User obj = new User();
obj.UserName = model.Email;
obj.FirstName = model.FirstName;
obj.LastName = model.LastName;
obj.Email = model.Email;
obj.Phone = model.Phone;
obj.Notes = model.Notes;
obj.Authorized = model.Authorized;
UserRepository.SaveUser(obj);
TempData["Success"] = "Yes";
}
return View(model);
}
View
#Html.ValidationSummary()
#if (TempData["Success"] != null)
{
<div class="alert alert-success">
×
<strong>Success!</strong> The User account was created.
</div>
}

Normally when the result of an action method is successful a redirect happens, maybe that's what you want, especially if your result is not a json result. But if you are returning the same view after your post then you are doing it incorrectly. If the ModelState is valid on a post, that is if the validation passed (e.g. required fields are supplied), and you add an error message by doing ModelState.AddModelError(("", "Data successfully saved.") then you are making the ModelState go into an invalid state. That is the reason why you have the red color.
Now assuming you really want to return the same view then I suppose you have something like:
[HttpPost]
public ActionResult YourActionMethod(YourModel model)
{
// some code goes here
ModelState.AddModelError(("", "Data successfully saved.")
return View(", model);
}
What you should have instead is something like this:
[HttpPost]
public ActionResult YourActionMethod(YourModel model)
{
// some code goes here
ViewBag.SuccessMessage = "Data successfully saved.";
return View(", model);
}
Then on your view something like:
#Html.ValidationSummary(true)
if (!string.IsNullOrWhiteSpace(ViewBag.SuccessMessage)) {
<div class="success-summary">
<p>#ViewBag.SuccessMessage</p>
</div>
}
Note that you don't need an additional # before the if, that code assumes it's inside a form tag, using #using. And then for the css:
.success-summary {
color: #3366FF;
}
You can actually use either ViewData or ViewBag. To know more about the difference of the two you can visit this SO page.
UPDATE:
[HttpPost]
public ActionResult YourActionMethod(YourModel model)
{
//
If (ModelState.IsValid) {
#ViewBag.IsModelValid = true;
ModelState.AddModelError("", "Data successfully saved.");
return View(model);
}
ViewBag.SuccessMessage = "Data successfully saved.";
return View(", model);
}
Your view:
#Html.ValidationSummary(false, "", new { #class= (ViewBag.IsModelValid!=null && ViewBag.IsModelValid) ? "success-summary" : "" })

Von, I too appreciate your answer, but I agree with MaxPayne that you didn't quite provide an answer for the question, more of a work around IMO.
I too am looking at a way to style the ValidationSummary without the extra baggage of using the ViewBag.
I do agree that you shouldn't return to the same view after a post unless there are errors, but I do believe there are times when one might want to change the ValidationSummary style dynamically without having to use the ViewBag.
So far this is my only lead http://westcountrydeveloper.wordpress.com/2011/07/06/mvc-validation-part-4-styling-the-validation-controls/
I suppose you could use some JQuery to change the element's css attributes based on the Validation response.
var valid = $("#formID").validate().element("#ElementID");
//or
var valid = $('#formID').validate();
// Then use $(".ElementClass").css({}); to change the the style

Related

Loding pages by posting parameters

The subject might not be clear since I couldn't find a better way to express it.
I am developing a web application using ASP.NET Core 6.0 with Razor Pages. Our previous application was an SPA using Ext JS where any call to server was returning only data and where I was also able to make any kind of call (GET/POST) to get the data.
For example, in the above picture from my old application, I make an ajax call with POST to get the list of periods when I open this page. I make a POST because I am sending the period type in my request payload. Sure I can pass these parameters in a GET request, however my other views have many criteria, so passing these criteria in the query string is not what I want. So, I decided to make it a standard to make my calls with POST method if there are any criteria payload, make GET request only when fething an entity with a simple key parameter (like Id) or GET any list that doesn't have any criteria.
Now, I am quite confused how to do same thing in my new ASP.NET Core Razor Pages web application. Normally, the menu items navigate to the page using link as below, which makes a GET request:
<a asp-area="System" asp-page="/ProfessionList">#AppLocalizer["Profession List"]</a>
<a asp-area="System" asp-page="/PeriodList">#AppLocalizer["Profession List"]</a>
In order to make a POST request, I replaced the menu item for period list as following which makes a POST request with a default periodType payload:
<a asp-area="System" asp-page="/ProfessionList">#AppLocalizer["Profession List"]</a>
<form asp-area="System" asp-page="/PeriodList" method="post">
<input type="hidden" name="periodType" value="1" hidden />
<button type="submit" >#AppLocalizer["Period List"]</button>
</form>
And the corresponding PeriodType.cshtml.cs file is as following:
[Authorize]
public class PeriodListModel: BaseEntityListPageModel<List<JsonPeriodEx>> {
public PeriodListModel(ILogger<BaseEntityListPageModel<List<JsonPeriodEx>>> logger, WebApi webApi) : base(logger, webApi) {
}
public IActionResult OnGet() {
PageData = JsonConvert.DeserializeObject<List<JsonPeriodEx>>(TempData["PageData"].ToString());
return Page();
}
public async Task<IActionResult> OnPostAsync(int periodType) {
var jsonResult = await _WebApi.DoPostAsync<List<JsonPeriodEx>>("/PeriodEx/GetList", new[] { new { Property = "periodType", Value = periodType } });
if (jsonResult.IsLoggedOut)
return RedirectToPage("/Login", new { area = "Account" });
if (jsonResult.Success) {
PageData = jsonResult.Data;
TempData["PageData"] = JsonConvert.SerializeObject(PageData);
return RedirectToPage("/PeriodList");
} else {
return RedirectToPage("/Error");
}
}
}
OnPostAsync successfully binds to the posted periodType parameter and gets the list of periods. Now, at the end of a successful call I want to follow the Post/Redirect/Get pattern and redirect to OnGet with the data from OnPostAsync, which is stored in TempData.
Now, according to the above scenario, is my approach, explained above, correct or should I implement it differently?
Thanks in advance
For these cases I would prefer TempData. Much easier and less code.
public async Task OnGet()
{
TempData["myParamToPass"] = 999;
...
}
public async Task OnPostReadData()
{
if (TempData.ContainsKey("myParamToPass"))
{
var myParamToPassValue = TempData.Peek("myParamToPass") as int?;
...
}
...
}

Entity FrameWork Core does not display the value returned through querying

I'm trying to display a specific value from a column on the page and I'm using ASP.NET Core MVC with Entity FrameWork Core to do so. The problem which I'm facing that it does not return the the column value, it rather returns some method.
Here's the controller
public IActionResult newTicket()
{
string getUsername = HttpContext.Session.GetString("username");
ViewBag.username = _context.users.Where(o => o.username == getUsername).ToList();
return View();
}
So basically what this action should be doing that it should get username from the session first. Then, it should run a query in the database such that if there is a similar username in the database then pass the value of username to the view by using viewbag.
The view
<input class="form-control w-50" asp-for="cName" required="required" value="#ViewBag.username"/>
This is how the output looks, I don't want it to look like that, I want it to display username.
https://i.stack.imgur.com/n6dXo.png
If I had to explain what I mean in this question through another language (PHP MySQL) is that.
$query = mysqli_query($connection_variable,"SELECT * from complaints
WHERE username = '$username'");
while ($row = mysqli_fetch_assoc($query))
{
echo $row['username'];
}
This is what I mean, in case I failed to explain you can ask for details.
Your ViewBag use List, not user data.
Use this.
public IActionResult newTicket()
{
string getUsername = HttpContext.Session.GetString("username");
ViewBag.username = _context.users.Where(o => o.username == getUsername).FirstOrDefault()?.username;
return View();
}
What you can do in view something like this:
#foreach(User usr in ViewBag.username)
{
<b>#usr.username</b>
}
But now what you are doing is just calling the default ToString() method of collection. That's why you are getting that result. You should be calling the property of an element in that collection.
Or you can try this:
ViewBag.username = _context.users.FirstOrDefault(o => o.username == getUsername);

Validation messages from custom model validation attributes are locked to first loaded language

I am working on a multi lingual website using Umbraco 7.2.4 (.NET MVC 4.5). I have pages for each language nested under home nodes with their own culture:
Home (language selection)
nl-BE
some page
some other page
my form page
fr-BE
some page
some other page
my form page
The form model is decorated with validation attributes that I needed to translate for each language. I found a Github project, Umbraco Validation Attributes that extends decoration attributes to retrieve validation messages from Umbraco dictionary items. It works fine for page content but not validation messages.
The issue
land on nl-BE/form
field labels are shown in dutch (nl-BE)
submit invalid form
validation messages are shown in dutch (nl-BE culture)
browse to fr-BE/form
field labels are shown in french (fr-BE)
submit invalid form
Expected behavior is: validation messages are shown in french (fr-BE culture)
Actual behavior is: messages are still shown in dutch (data-val-required attribute is in dutch in the source of the page)
Investigation to date
This is not a browser cache issue, it is reproducible across separate browsers, even separate computers: whoever is generating the form for the first time will lock the validation message culture. The only way to change the language of the validation messages is to recycle the Application Pool.
I doubt that the Umbraco Validation helper class is the issue here but I'm out of ideas, so any insight is appreciated.
Source code
Model
public class MyFormViewModel : RenderModel
{
public class PersonalDetails
{
[UmbracoDisplayName("FORMS_FIRST_NAME")]
[UmbracoRequired("FORMS_FIELD_REQUIRED_ERROR")]
public String FirstName { get; set; }
}
}
View
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
var model = new MyFormViewModel();
using (Html.BeginUmbracoForm<MyFormController>("SubmitMyForm", null, new {id = "my-form"}))
{
<h3>#LanguageHelper.GetDictionaryItem("FORMS_HEADER_PERSONAL_DETAILS")</h3>
<div class="field-wrapper">
#Html.LabelFor(m => model.PersonalDetails.FirstName)
<div class="input-wrapper">
#Html.TextBoxFor(m => model.PersonalDetails.FirstName)
#Html.ValidationMessageFor(m => model.PersonalDetails.FirstName)
</div>
</div>
note: I have used the native MVC Html.BeginForm method as well, same results.
Controller
public ActionResult SubmitFranchiseApplication(FranchiseFormViewModel viewModel)
{
if (!ModelState.IsValid)
{
TempData["Message"] = LanguageHelper.GetDictionaryItem("FORMS_VALIDATION_FAILED_MESSAGE");
foreach (ModelState modelState in ViewData.ModelState.Values)
{
foreach (ModelError error in modelState.Errors)
{
TempData["Message"] += "<br/>" + error.ErrorMessage;
}
}
return RedirectToCurrentUmbracoPage();
}
}
LanguageHelper
public class LanguageHelper
{
public static string CurrentCulture
{
get
{
return UmbracoContext.Current.PublishedContentRequest.Culture.ToString();
// I also tried using the thread culture
return System.Threading.Thread.CurrentThread.CurrentCulture.ToString();
}
}
public static string GetDictionaryItem(string key)
{
var value = library.GetDictionaryItem(key);
return string.IsNullOrEmpty(value) ? key : value;
}
}
So I finally found a workaround. In attempt to reduce my app to its simplest form and debug it, I ended up recreating the "UmbracoRequired" decoration attribute. The issue appeared when ErrorMessage was set in the Constructor rather than in the GetValidationRules method. It seems that MVC is caching the result of the constructor rather than invoking it again every time the form is loaded. Adding a dynamic property to the UmbracoRequired class for ErrorMessage also works.
Here's how my custom class looks like in the end.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
AllowMultiple = false)]
internal class LocalisedRequiredAttribute : RequiredAttribute, IClientValidatable
{
private string _dictionaryKey;
public LocalisedRequiredAttribute(string dictionaryKey)
{
_dictionaryKey = dictionaryKey;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
ModelMetadata metadata, ControllerContext context)
{
ErrorMessage = LanguageHelper.GetDictionaryItem(_dictionaryKey); // this needs to be set here in order to refresh the translation every time
yield return new ModelClientValidationRule
{
ErrorMessage = this.ErrorMessage, // if you invoke the LanguageHelper here, the result gets cached and you're locked to the current language
ValidationType = "required"
};
}
}

The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[X]', but this dictionary requires a model item of type 'X'

*CORRECTION
The problem occurs when my view is called to populate a list from my user table.
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[Mike.Models.User]', but this dictionary requires a model item of type 'Mike.Models.User'.
Here is my controller action:
public ActionResult Registration(Mike.Models.User user)
{
if (ModelState.IsValid)
{
using (var db = new UserContext())
{
var crypto = new SimpleCrypto.PBKDF2();
var encrypPass = crypto.Compute(user.password);
var sysUser = db.Users.Create();
sysUser.LastName = user.LastName;
sysUser.FirstName = user.FirstName;
sysUser.Email = user.Email;
sysUser.password = encrypPass;
sysUser.passwordSalt = crypto.Salt;
sysUser.UserID = user.UserID;
db.Users.Add(sysUser);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
}
return View(user);
}
Can someone please help me.... There are responses to similar questions on the internet but I believe mine is unique.. I have searched for weeks to no avail.
Thanks in advance,
Renior
Here is my simple controller action...
public ActionResult Index()
{
return View(db.Users.ToList());
}
and my razor syntax.
#model IEnumerable
Im trying to populate a view of my user table list..
In your Registration view at the top where your model declaration is, instead of this:
#model List<Mike.Models.User>
you need to have:
#model Mike.Models.User
You probably used strongly typed scaffolding feature to generate your view but instead of details option you chose a list option...
Take this at face value - yours is not unique. Your problem is you are passing an array of user to a controller action that expects a user.
You need to post your HTML but it is probably something like #model List user or something instead of a single user.
If your model represents a single user then pass that to the controller. If opposite, do opposite,
If you want to pass a list to the controller use list users
edit
make your razor syntax
#model Mike.Models.User

Persisting MVC4 controller data thru multiple post backs

I have a MVC4 controller that calls its view multiple times, each time with a different set of ViewBags. The view renders the contents of a Form based on the absence or presence of those ViewBags via something like this:
#using (Html.BeginForm())
{
#if (ViewBag.Foo1 != null)
{
#Html.DropDownList("Bar1",....
}
#if (ViewBag.Foo2 != null)
{
#Html.DropDownList("Bar2",....
}
<input name="ActionButton" type="submit" value="Ok"" />
}
Each time the user clicks the submit button, the controller checks to see what is in the collection and makes a new set of ViewBags before calling the view again, sort of like this:
public ActionResult Create()
{
ViewBag.Foo1 = "blawblaw";
return View();
}
[HttpPost]
public ActionResult Create(FormCollection collection)
{
if (collection["Bar1"] != null)
{
string FirstPass = collection["Bar1"];
ViewBag.Foo2 = "blawblaw";
}
if (collection["Bar2"] != null)
{
string SecondPass = collection["Bar2"];
ViewBag.Foo3 = "blawblaw";
}
return View();
}
What I need to do now is somehow have each pass thu the controller 'remember' something about its previous passes. That is, in my example, the second pass thru the controller (the one where collection["Bar2"] is true), the value of FirstPass is null.
How can I do that?
In that case have a look at best practices for implementing a wizard in MVC. Some good suggestions here. Personally I would still consider using separate and distinct urls. Also, If you have db access in your solution you can still store temporary data before updating the main model. Think about what you want to happen if the user doesn't complete the whole journey the first time round...