How to pass error messages to Views? - asp.net-mvc-3

I want to display error messages in my view. What is the best way to do this?
What replaces the "???" in my code below? I don't want to simply use Html.ValdiationSummary because right now I'm thinking I need to process the list myself and place certain error messages in different places. For example, the code below would actually need to be expanded to put some of the error messages in a floating div, while others may be displayed at the top of the page.
Is there a better way to do this altogether? e.g. Should I be using an entirely different approach to passing error messages from my controller to the view?
My Controller:
public ActionResult ForgotUsername(ForgotUsernameModel model)
{
...
if (!Users.CheckUsername(model.UserName)) {
ModelState.AddModelError("", "That username does not exist.");
}
....
return View(model);
}
My View:
....
<%
if (???) {
foreach (KeyValuePair<string, ModelState> item in ViewData.ModelState) {
if (item.Value != null && item.Value.Errors != null && item.Value.Errors.Count > 0) {
foreach (ModelError e in item.Value.Errors) {
Response.Write(String.Format("<div>{0}</div>", e.ErrorMessage));
}
}
}
}
%>

<% if (!ViewData.ModelState.IsValid) { %>
<%= Html.ValidationSummary(true) %>
<% } %>
or simply:
<%= Html.ValidationSummary(true) %>

Related

Silverstripe Sessions or URL Parameters

I'm trying to figure out how to pass a value from my .ss page to my controller for a custom search filter that I've built. The idea is you click on this image or a form button and then the page will set a session variable and refresh itself. Upon page load the page loads different information depending on what it reads in the session variable. I can accomplish the same thing with URL Parameters but there are no examples online that I could find that would show me how to do this.
Basically I have this as my php:
class ArticleHolder_Controller extends Page_Controller {
public function ValidateType(){
if(isset($_SESSION['mySearchTag']) && !empty($_SESSION['mySearchTag'])) {
$tag = $_SESSION['mySearchTag'];
}
else{
$tag='News';
}
$filter = $this::get()->filter('Filters:PartialMatch', $tag)->First();
if ($filter == NULL){
return NULL;
}
else{
$_SESSION['mySearchTag']=$tag;
return $this->PaginatedPages();
}
}
public function PaginatedPages(){
$paginatedItems = new PaginatedList($this->filterArticles($_SESSION['mySearchTag']), $this->request);
$paginatedItems->setPageLength(3);
return $paginatedItems;
}
public function filterArticles($tag){
return ArticlePage::get()->filter('category:PartialMatch', $tag)->sort('Date DESC');
}
}
my .ss looks like this:
<% if ValidateType() %>
<ul>
<% loop $PaginatedPages %>
<li>
<div class="article">
<h2>$Title</h2>
<h3>$Date</h3>
<img class="indent" src="$Photo.link" alt="image"/>
<p class="indent">$Teaser</p>
</div>
</li>
<% end_loop %>
</ul>
<% include Pagination %>
<% else %>
<p>SORRY NO RESULTS WERE FOUND</p>
<% end_if %>
This code works as is. What I cannot figure out how to now add a clickable button on .ss page that will reload the page and set a session variable value.
If I can achieve this with url parameters then that can work too, I just need to know how to set them in the .ss page and how to retrieve them in the php.
Create a SetFilter function that will take the URL parameter ID and set it to your session variable:
public function SetFilter() {
if($this->request->param('ID')) {
$_SESSION['mySearchTag'] = $this->request->param('ID');
}
return array();
}
Make sure your SetFilter function is added to the $allowed_actions of your controller:
static $allowed_actions = array (
'SetFilter'
);
This function is called by your page link followed by /SetFilter/[your-filter].
In your template you would create a link to create this filter like so:
Filter articles by example

In mvc String is null or empty is not check the null values

I have Industry type in my database. When it is null, it is showing an error like:
"Nullable object must have a value."
I want my value to display empty when it is null too.
This is my code:
<p>
<strong>Industry Type:</strong>
<%: Model.GetIndustry(Model.IndustryId.Value).Name%>
</p>
Anyone have an idea? Please help me...
It is similar to "von v"'s answer, but I think the error of "Nullable object must have a value." can be from IndustryId since is nullable, so it is good to check for that first:
<p>
<strong>Industry Type:</strong>
<%:if(Model.IndustryId.HasValue)
{
var idustry = Model.GetIndustry(Model.IndustryId.Value);
if(industry!= null)
industry.Name
}
else
{
""
}
%>
</p>
In my view it is good to do this
Model.GetIndustry()
in Back end, like controller, and then return the industry by viewstate,
like checking in the:
string industryName = "";
if(Industry.IndustryId.HasValue){
var industry = YourClass.GetIndustry(Industry.IndustryId.Value);
industryName = industry!= null ? Industry.Name : "" ;
}
ViewBag.IndustryName= industryName;
and then use your ViewBag in view like this:
<p>
<strong>Industry Type:</strong>
<%: ViewBag.IndustryName %>
</p>
It is good to separate the checking from view and do the logics in code.
Hope it helps.
Check that an industry is returned first then write the name if there's any
<%
var industry = Model.GetIndustry(Model.IndustryId.Value);
%>
<%: industry == null ? "" : industry.Name %>
Is IndustryId nullable? Then you can do this
<%
var industry = Model.GetIndustry(Model.IndustryId.GetValueOrDefault(0));
%>
and in the GetIndustry method, you can just return null if the id is zero.
public Industry GetIndustry(int id) {
if (id==0) return null;
// else do your stuff here and return a valid industry
}
string can accept null or empty value, why don't you try string.IsNullOrEmpty(Model.IndustryId.Value)
this is pretty self explanatory. in your controller,
string yourValue = string.empty;
yourValue = Model.GetIndustry(Model.IndustryId.Value).Name;
if(string.IsNullOrEmpty(yourValue))
{
//display your result here!
}
ViewBag.IndustryValue = yourValue;
Please try something along the lines of
<p>
<strong>Industry Type:</strong>
<%: Model.IndustryId.HasValue ? Model.GetIndustry(Model.IndustryId.Value).Name : string.Empty%>
</p>
I found the Problem.
<p>
<strong>Industry Type:</strong>
<% if (Model.IndustryId.HasValue)
{ %>
<%: Model.GetIndustry(Model.IndustryId.Value).Name%>
<%}
else
{ %>
<%:Model.IndustryId==null ? "": Model.GetIndustry(Model.IndustryId.Value).Name %>
<%} %>
</p>

What is the best way to access a model from a view when the model is optional?

I have a single view that is used by two Controller Actions.
My Controller:
AccountController {
...
ActionResult LogOn() {
return View()
}
[HttpPost]
ActionResult LogOn(LogOnModel model) {
return View(model)
}
}
My View:
<% if (!String.IsNullOrEmpty(Model.UserName)) { %>
<div class="username"><% Response.Write(Model.UserName); %></div>
<% } %>
Of course, the problem with the above code is that I get an error "Object Reference not set to an instance of an object" on the line that references Model.UserName.
What is the best way to my view to support both Actions? I can replace the HttpGet LogOn method with:
ActionResult LogOn() {
result View(new LogOnModel());
}
That works, but it doesn't seem very elegant and I'm sure an empty model will break some more complex models that I will need to create later on. Is there a better way? Thanks!
What is the best way to my view to support both Actions?
If a view is strongly typed to a view model (which is what absolutely all your views should be) you should make sure that all your controller actions serving those views (which is pretty much all your controller actions in your application) are passing a view model to those views.
Which is pretty much bla-bla which translated into source code pretty basically means this:
ActionResult LogOn() {
return View(new LogOnModel())
}
That works, but it doesn't seem very elegant and I'm sure an empty
model will break some more complex models that I will need to create
later on.
That's the correct way. Don't worry. You ain't gonna break anything. It's by not providing a view model to your views that you are breaking stuff.
Also as a side note you should absolutely never use Response.Write in a view. So instead of:
<% Response.Write(Model.UserName); %>
you totally want:
<%= Html.DisplayFor(x => x.UserName) %>
or:
<%: Model.UserName %>
but personally I prefer the DisplayFor helper since it will take care of any metadata.
You can either return a different view or as you have done, return a new model.
As noted by other commenters, your Controller should support the strong-typeness of your view. But if you insist on proceeding with the anti-pattern. Replace this:
<% if (!String.IsNullOrEmpty(Model.UserName)) { %>
<div class="username"><% Response.Write(Model.UserName); %></div>
<% } %>
with this:
<% if (Model!= null && !String.IsNullOrEmpty(Model.UserName)) { %>
<div class="username"><% Response.Write(Model.UserName); %></div>
<% } %>

View Database Columns via Checkbox WebFrom and Cookies

I am using the telerik MVC template and have a database that has a huge number of columns and the telerik grid does not have a horizontal scroll bar, so I created checkboxes for the user to select exactly what columns they want to view. It works well enough in that when I first go to the page it shows the checkboxes at top with the Apply button and the grid view underneath. Because nothing has been submitted from the WebForm, the grid view shows all the columns. Before adding the cookies, the user only had to press apply once for only those columns to appear. However, if the user then tried to sort or filter one of these columns, it would revert back to showing all of the columns. So, I created a cookie to store the selected information. Unfortunately, this only helped with the selection of the first filter. If a second filter is used, it would, again, show all of the columns instead of just the ones selected. Furthermore, the user now has to press apply twice for their selections to show properly on the grid view. Here is a brief explanation of how I have everything coded:
Index View
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm("Index", "Order"))
{ %>
<p>
<%= Html.CheckBox("osurr", true, "Ad Number")%>Ad Number //I set this and a few other columns to default to true
<%= Html.CheckBox("csurr", false, "Customer Number")%>Customer Number
<%= Html.CheckBox("rhosurr", false, "RHO Number")%>RHO Number
<%= Html.CheckBox("lockid", false, "Lock ID")%>Lock ID
//And several more
</p>
<input type="submit" value="Apply" />
<% } %>
<%
Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
columns.Template(o =>
{
%>
<%=Html.ActionLink("Detail", "Edit", new { id = o.osurr })%>
<%
}).Width(25);
if (Request.Cookies["DBCols"]["csurr"] != null)
{
if (Request.Cookies["DBCols"].Values["csurr"].Equals("True")) { columns.Bound(o => o.csurr).Title("Cust. No."); }
}
if (Request.Cookies["DBCols"]["rhosurr"] != null)
{
if (Request.Cookies["DBCols"].Values["rhosurr"].Equals("True")) { columns.Bound(o => o.rhosurr).Title("RHO No."); }
}
if (Request.Cookies["DBCols"]["lockid"] != null)
{
if (Request.Cookies["DBCols"].Values["lockid"].Equals("True")) { columns.Bound(o => o.lockid).Title("Lock ID"); }
}
//And again, several more.
})
.Groupable(grouping => grouping.Enabled(true))
.Resizable(resizing => resizing.Columns(true))
.Filterable(filter => filter.Enabled(true))
.Sortable(sorting => sorting.Enabled(true))
.Pageable(paging => paging.Enabled(true).PageSize(25))
.Render();
%>
</asp:Content>
Controller
public ActionResult Index(bool? csurr, bool? rhosurr, bool? lockid /* And Several More */)
{
ViewData["csurr"] = csurr ?? true;
ViewData["rhosurr"] = rhosurr ?? true;
ViewData["lockid"] = lockid ?? true;
if ((bool)ViewData["csurr"]) { DBCols.Values["csurr"] = (ViewData["csurr"].ToString());
}
else { DBCols.Values["csurr"] = "False"; }
if ((bool)ViewData["rhosurr"]) { DBCols.Values["rhosurr"] = (ViewData["rhosurr"].ToString()); }
else { DBCols.Values["rhosurr"] = "False"; }
if ((bool)ViewData["lockid"]) { DBCols.Values["lockid"] = (ViewData["lockid"].ToString()); }
else { DBCols.Values["lockid"] = "False"; }
//And Several more
var db = new MillieOrderDB();
var listView = from m in db.vw_cadords
orderby m.createstamp descending
select m;
return View(listView);
}
I am working just in the Index ActionResult for now to keep things in one place while I figure out how to get this all to work. Anyone have any ideas why I am having to press apply twice, why I can not use more than one filter, and how to avoid this?
It turns out that the reason I had to hit apply twice and why when applying more than one filter caused issues was because I had everything in the Index ActionResult. Once I moved all the form data to its own ActionResult and then did RedirecttoAction("Index"), everything worked fine!

asp.net mvc 2 client side validation missing ValidationRules on custom attribute

Can't seem to get checkbox to be validate on client-side using asp.net mvc 2. Here is my code.
Model
[Serializable]
public class RegistrationModel
{
bool termsAndCondition = false;
[RequiredToBeTrue(ErrorMessage = "Need terms and service")]
public bool TermsAndConditions
{
get
{
return termsAndCondition;
}
set
{
termsAndCondition = value;
}
}
}
Custom Attribute
public class RequiredToBeTrueAttribute : RequiredAttribute
{
public override bool IsValid(object value)
{
return (value != null) && (value is bool) ? (bool)value : false;
}
}
View
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<RegistrationModel>" %>
<% Html.EnableClientValidation(); %>
<% using (Html.BeginForm("Register", "Registration", new { area="Account", id = "openid_form", inRegistration = true }))
<%=Html.ValidationSummary(false) %>
blah blah blah
<div class="checkbox"><label><%= Html.CheckBoxFor(model => model.TermsAndConditions) %>I agree to the terms and conditions of use.</label></div>
<input type="submit" id="submit" name="submit" value="Join Now" />
<%
Html.ValidateFor(m => m.TermsAndConditions);
%>
<% } %>
I am trying to call Html.ValidateFor at the end to push up all error message at top of the page. However, the property "TermsAndConditions" is not getting validated on client side (works great on server side). This leads me to look at the the window.mvcClientValidationMetData method at that mvc push out and I saw the following:
{"FieldName":"TermsAndConditions","ReplaceValidationMessageContents":false,"ValidationMessageId":null,"ValidationRules":[]}
Which you can see that "ValidationRules" are empty meaning that it is trying to validate it but the error message wasn't push out to the client for some reason.
Any ideas? Any help is appreciated.
Seems like I need to do more digging first. Was hoping the new attribute will appear magically on the client side. Instead, have to write some customer javascript to wire it up. See phil hack's post for detail.
This article from Phil Haack, ASP.NET MVC 2 Custom Validation, should help point you in the right direction.
Basically you need to create your own DataAnnotationsModelValidator<RequiredToBeTrueAttribute> and then write some client side script to get it done.
HTHs,
Charles

Resources