I've got some POCO Model classes that I've setup for use with the Entity Framework. I do some validation in my DbContext's ValidateEntity override. I return a DbEntityValidationResult from the ValidateEntity function, and I can see that during run-time I do add some DbValidationErrors. I can even see those errors inside of the ModelState inside of my Controller function, using the following code ...
catch (DbEntityValidationException ex)
{
foreach (var entity in ex.EntityValidationErrors)
{
foreach (var error in entity.ValidationErrors)
{
ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
}
}
}
But for some reason those errors don't show up for the desired property name in the Razor view. I use a view model that looks like the following ...
public class CharacterCreateModel
{
private Character m_character;
#region Properties
public Character Character
{
get
{
return m_character;
}
set
{
m_character = value;
}
}
#endregion
}
And in my Razor view, which is strongly typed using this CharacterCreateModel view mode, I just use the standard #Html.TextBoxFor, etc.
Validation errors coming from the Character model properly display, but validation errors from the ValidateEntity function are not showing up for that property name.
Any idea why not?
You need to include the ValidationMessageFor helper in your code to show model level properties.
You should have
#Html.EditorFor(model => model.Character)
#Html.ValidationMessageFor(model => model.Character)
If this in not showing the errors change the
#Html.ValidationSummary(true)
at the top of your view to
#Html.ValidationSummary(false)
so you can see all the validation errors and make sure they're actually being added correctly.
Related
I have the following code:
View:
#model IEnumerable<FormBuilder.Model.Formular>
<div id="divAktiveFormulare">
#{ Html.RenderPartial("Formular/TableAllFormulare", Model.Where(model => model.Status == FormularStatus.Aktiv).ToList()); }
</div>
<div id="divArchivierteFormulare">
#{ Html.RenderPartial("Formular/TableAllFormulare", Model.Where(model => model.Status == FormularStatus.Archiviert).ToList()); }
</div>
function btnArchivieren(e) {
$.ajax({
url: 'Url.Action("Archive", "Formular")',
data: { id: e.target.name },
success: function (data) {
$('#divArchivierteFormulare').html(data);
}
});
}
Controller:
public ActionResult Index()
{
List<Formular> aktiveformulare = _formularManager.GetAllFormulare();
return View(aktiveformulare);
}
public ActionResult Archive(int id)
{
Formular formular = _formularManager.GetFormularByID(id);
if (formular != null)
{
formular.Status = FormularStatus.Archiviert;
_formularManager.UpdateFormular(formular);
}
return PartialView("Formular/TableAllFormulare", _formularManager.GetAllFormulare());
}
I have two divs (divAktiveFormulare and divArchivierteFormulare). In each of them a partial view is rendered. The partial views show a table of the model-items (model is an IEnumerable), and each has a filtered model after an enumaration (f.e. Html.RenderPartial("Formular/TableAllFormulare", model => model.Status == FormularStatus.Archiviert).
While loading the index-page for the first time, everything works perfectly. I can set a breakpoint at the RenderPartial()-lines and it will stop there. Both partial views show its tables correctly after the filtered model.
On my page I have a button and by pressing it it will start the Javascript btnArchivieren, which send an ajax to the controller. The action will return a Partial View() with the updated model.
But now my brakepoint at RenderPartial() will not be fired again. Well, the div is updated yeah, but without filtering the model for the partial views (Model.Where(model => model.Status == FormularStatus.Archiviert). Both partial views are showing the table without filtering by enumaration. Like like i said, my brakepoint wasn't even fired.
What am I doing wrong?
Why aren't the RenderPartial()-lines executed after returning the partial view from the controller with an updated model?
When you use :
#{ Html.RenderPartial("Formular/TableAllFormulare", Model.Where(model => model.Status == FormularStatus.Aktiv).ToList()); }
You are telling your application that it has to call your partial view "Formular/TableAllFormulare" with the data you provided.
It's done only while rendering all the page, not by calling it in ajax.
It's the code inside that partial view that will be executed by the ajax call.
Anyway, I would suggest to place your breakpoint in your controller at this line :
Formular formular = _formularManager.GetFormularByID(id);
For it's that line that will be exectued, and probably where the error is hiding.
EDIT :
You'll just have to do that :
return PartialView("Formular/TableAllFormulare", _formularManager.GetAllFormulare().Where(model => model.Status == FormularStatus.Archiviert).ToList());
I hope you understand why it won't work as you expected it at first, so tell me if you don't. I'll try to explain it better.
I was wondering if there's a way to specify a model for the _layout.cshtml file, i've seen lots of posts with the basic same question with people replying with "alternative" solutions, not saying it's not possible nor showing how exactly we could achieve this
having some experience with webforms I've been trying to migrate to MVC and often find myself with such questions, I've found this website: http://blog.bitdiff.com/2012/05/sharing-common-view-model-data-in.html
which partially solved my problem but even them don't bind their _layout.cshtml with a #model, as far as I know, I have to specify a model on each view if I want to access the SharedContext, please correct if I'm wrong
what I wanted to do is declare a "#model Namespace.MyModel" on _layout.cshtml so it could retrieve its information by itself, instead of having to implement a model for each view inherinting from the LayoutModel
*I hope I'm being clear, basically, I wanted to know how can I declare #model tag on a _layout.cshtml so it can access its own model
with the solution I linked before (even though it's not linked to my question) I have to do:
#(((BaseController)ViewContext.Controller).Context.Property) to get the shared information, and if I could simply declare (and use) a #model instead, I could accomplish the same thing by doing something like: #Model.Property*
as you can see, im struggling trying to migrate whatever I already know from webforms to MVC and it's being quite difficult for me since I have to adopt certain practices which are completely different from what I'm used to
thanks in advance
You should delegate the parts of your layout that "need a model" to a separate controller using partial views and RenderAction:
#Html.RenderAction("SomeAction", "LayoutController")
Have LayoutController.SomeAction return a PartialViewResult, which you can then strongly type to a model.
Even though you already accepted an answer, based on your saying you are just pulling an image URL you should do it using JQuery, not a model.
This code is untested, apologies for that. Feel free to point out if I typed a bug. The HTML element containing the background image has the id="url" attribute so the selectors work.
Controller
[HttpGet]
public string GetSessionUrl()
{
//logic to detmine url
return url;
}
JQuery
$(document).ready(function () {
var $url = $('#url');
var options = {
url: "/Home/GetSessionUrl",
type: "get",
async:false
};
$.ajax(options).done(function (data) {
$url.attr('src', data);
});
});
You can add BaseModel to _Layout.
#model BaseModel
Then all models inherit from that BaseModel class.
public class MyModel : BaseModel
{
}
As others stated, it is not a good practice. If your model forgets to inherit from BaseModel, it'll throws exception at run time. However, it is up to you.
In BaseController you can declare any model as property.
public class BaseController : Controller
{
public BaseController ()
{
MyTag = new TagModel (); // or get db, take any value from there
}
public TagModel MyTag { get; set; }
}
In action:
ViewBag.MyTag = MyTag ;
And in _Layout.cshtml, you can use
#{
var myTag = (TagModel)ViewBag.MyTag;
}
I used the following tutorial to help me build an RSS Reader in my ASP.NET MVC3 Razor application:
http://weblogs.asp.net/jalpeshpvadgama/archive/2011/08/17/creating-basic-rss-reader-in-asp-net-mvc-3.aspx
However, unlike the tutorial example, I want the RSS feed to be displayed on every page, and have therefore added it to my layout file, /Views/Shared/_Layout.cshtml
I currently only have 2 views on my site, and to get the RSS Reader to work on both views I've got the following code in my HomeController:
public class HomeController : Controller
{
//
// GET: /Index/
public ActionResult Index()
{
return View(CT.Models.RssReader.GetRssFeed());
}
public ActionResult About()
{
return View(CT.Models.RssReader.GetRssFeed());
}
}
From my WebForms experience, I would simply add the RSS Reader code in my master page code behind, and it would automatically work on every page.
Is there a Controller for layout pages which allows me to do the same?
How can I get this to work on every call of the layout page, without having to return anything?
EDIT: Following #Sebastian's advice, I've now added this code to a Partial View, removed CT.Models.RssReader.GetRssFeed() from return View() and included this in my layout file:
#Html.Partial("_MyPartialView")
The code in this partial view is:
<ul>
#foreach (var item in Model)
{
<li>
#item.Title
</li>
}
</ul>
However, I'm not getting a runtime error:
Object reference not set to an instance of an object.
It's erroring on the line #foreach (var item in Model)
You have to create a partial view and add functionality there.
Then in your layout, render this partial.
EDIT
Is your partial view really a partial view? The reason I said that is because you have "_" in front of the name which suggests that it might be a layout (might just be a naming convention).
To fix object reference error, you have to add the #Model declaration on top of your partial view.
Hope it helps.
UPDATE
In order to use different model in partial view, you need to explicitly declare which model you are going to use on render partialmethod.
#{Html.RenderPartial("../YourFeed", Model.YourFeedModel);}
Let me know if that resolved your issue.
The new error you are having is due to you not passing a Model to the partial view. You can do this with the second argument of the Html.Partial function...
Html.Partial("ViewName", MyModel);
As I think you are trying to do this in a Layout page you could also consider using a static reference to get your RSS feed. So forget about needing to pass in a Model and in your partial have:
#foreach (var item in RssRepository.GetFeed())
{
<li>
#item.Title
</li>
}
this like to a class something like...
public static RssRepository
{
public static MyModel GetFeed()
{
return new MyModel();//<- return what you would normally pass as a Model for RSS feeds
}
}
Hope that all makes sense
I have a strongly typed view inheriting from a POCO class. I want to initialize the property of a model with a Querystring value at the time when view loads.
On the View Load I am using ViewData to the save the code :
public ActionResult Data() {
ViewData["QueryStringValue"] = this.Request.QueryString["Param1"]
return View();
}
In the HTML markup, I am using this code to initialize the model property in a hidden variable
<%:Html.HiddenFor(m=>m.Param,
Convert.ToInt32(Html.Encode(ViewData["QueryStringValue"]))) %>
m.param is a byte type.
URL of the request is somewhat like this : http://TestApp/Data/AddData?Param1=One
On View Save event, I am using model binding but issue is that I don't get to see the value of param initialized in the controller. It is always NULL.
My Save Event maps to a controller :
[HttpPost]
public ActionResult SaveData(MyData d)
{
string paramValue = d.Param; //this always returns null
BO.Save(d);
}
I inspected the HTML source and saw that the value of the hidden field itself is blank. Not sure why this is happening since the below code works and shows the param value in a heading element
<h2> <%=Html.Encode(ViewData["QueryStringValue"]) %> </h2>
I have no idea where I am going wrong on this.
I think, Instead of Passing the Querystring value in ViewData, You should set it as the Property value of your ViewModel/ Model and pass that to your View.
public ActionResult Data()
{
YourViewModel objVm=new YourViewModel();
objVm.Param=Request.QueryString["Param1"];
return View(objVm);
}
Now in your Strongly typed View, use it like this
#model YourViewModel
#using(Html.BeginForm())
{
#html.HiddenFor(#m=>m.Param);
<input type="submit" value="Save" />
}
Now Param value will be available in yout HttpPost action method
[HttpPost]
public ActionResult Data(YourViewModel objVm)
{
string param=objVm.Param;
//Do whatever you want with param
}
Just made this work, Issue is with this line:
<%:Html.HiddenFor(m=>m.Param,
Convert.ToInt32(Html.Encode(ViewData["QueryStringValue"]))) %>. I stated in the question that m.Param is of type byte. I figured out that issue was with casting.
I tried this code and it worked
<%:Html.HiddenFor(m => m.Param, (byte)Convert.ToInt16(this.Request.QueryString["Param1"].ToString()))%>
I have following problem. In my view model I defined some list properties as follows:
public class BasketAndOrderSearchCriteriaViewModel
{
List<KeyValuePair> currencies;
public ICollection<KeyValuePair> Currencies
{
get
{
if (this.currencies == null)
this.currencies = new List<KeyValuePair>();
return this.currencies;
}
}
List<KeyValuePair> deliverMethods;
public ICollection<KeyValuePair> DeliveryMethods
{
get
{
if (this.deliverMethods == null)
this.deliverMethods = new List<KeyValuePair>();
return this.deliverMethods;
}
}
}
This view model is embedded in another view model:
public class BasketAndOrderSearchViewModel
{
public BasketAndOrderSearchCriteriaViewModel Criteria
{
[System.Diagnostics.DebuggerStepThrough]
get { return this.criteria; }
}
}
I use 2 action methods; one is for the GET and the other for POST:
[HttpGet]
public ActionResult Search(BasketAndOrderSearchViewModel model){...}
[HttpPost]
public ActionResult SubmitSearch(BasketAndOrderSearchViewModel model){...}
In the view I implement the whole view model by using the EditorFor-Html Helper which does not want to automatically display DropDownLists for List properties!
1. Question: How can you let EditorFor display DropDownLists?
Since I could not figure out how to display DropDownLists by using EditorFor, I used the DropDownList Html helper and filled it through the view model as follows:
public IEnumerable<SelectListItem> DeliveryMethodAsSelectListItem()
{
List<SelectListItem> list = new List<SelectListItem>();
list.Add(new SelectListItem()
{
Selected = true,
Text = "<Choose Delivery method>",
Value = "0"
});
foreach (var item in this.DeliveryMethods)
{
list.Add(new SelectListItem()
{
Selected = false,
Text = item.Value,
Value = item.Key
});
}
return list;
}
My 2. question: As you can see I pass my view model to the action metho with POST attribute! Is there a way to get the selected value of a DropDownList get binded to the passed view model? At the moment all the DropDownList are empty and the selected value can only be fetched by the Request.Form which I definitely want to avoid!
I would greatly appreciate some ideas or tips on this!
For those like me that got to this post these days I'd recommend you to fully download the tutorial from http://www.asp.net/mvc/tutorials/mvc-music-store-part-1 which covers this and most of the common techniques related with .NET MVC applications.
Anyway Really usefull your post and answers man (If I could vote you I would :)
Let's try to take on this one:
Answer to Question 1: How can you let EditorFor display DropDownLists?
When you call Html.EditorFor() you can pass extra ViewData values to the EdiorTemplate View:
<%: Html.EditorFor(model => Model.Criteria, new { DeliveryMethods = Model.DeliveryMethods, Currencies = Model.Currencies}) %>
Now you have ViewData["DeliveryMethods"] and ViewData["Currencies"] initialized and available inside your EditorTemplate.
In your EditorTemplate you somehow need to call and convert those entries into DropDowns / SelectLists.
Assuming you've got an ascx file of type System.Web.Mvc.ViewUserControl<BasketAndOrderSearchCriteriaViewModel> you could do the following:
<%: Html.LabelFor(model => model.DeliveryMethods) %>
<%: Html.DropDownList("SelectedDeliveryMethod", new SelectList(ViewData["DeliveryMethods"] as IEnumerable, "SelectedDeliveryMethod", "Key", "value", Model.SelectedDeliveryMethod)) %>
Same goes for the Currencies.
<%: Html.LabelFor(model => model.Currencies) %>
<%: Html.DropDownList("SelectedCurrency", new SelectList(ViewData["Currencies"] as IEnumerable, "SelectedDeliveryMethod", "Key", "value", Model.SelectedCurrency)) %>
This setup will make your DeliveryMethodAsSelectListItem() obsolete and you can use any kind of list. Means you are not bound to KeyValuePairs. You'll just need to adjust your call on Html.DropDownList() from now on.
As you can see, I have introduced some new properties to your BasketAndOrderSearchCriteriaViewModel:
Model.SelectedDeliveryMethod
Model.SelectedCurrency
They are used to store the currently selected value.
Answer to Question 2: Is there a way to get the selected value of a DropDownList get binded to the passed view model?
In the EditorFor template we are passing the newly created Model.SelectedDeliveryMethod and Model.SelectedCurrency properties as the SelectedValue Parameter (See 4th Overload of the DropDownList Extension Method).
Now that we have the View doing it's job: How can we get the currently selected value inside the POST Action?
This is really easy now:
[HttpPost]
public ActionResult SubmitSearch(BasketAndOrderSearchViewModel model)
{
...
var selectedDeliveryMethod = model.Criteria.SelectedDeliveryMethod;
var selectedCurrency model.Criteria.SelectedDeliveryMethod;
...
}
Note: I don't have an IDE to test it right now, but it should do the trick or at least show you in which direction to go.