MVC - Model - View and Controller 's role - asp.net-mvc

Following is my Architecture of an application. I am confuse with whether I understood MVC correct or not? My Architecture is right or not?
View - interacts with the user, it has HTML part, on submit the form it calls controller
Controller - it checks for information added is valid or not? (not database point of view. it will just check for weather all the mandatory fields are filled or not?) decides which model to call.
Model - it contains class of view. and also some methods like add,modify or delete to deal with the database.
Is this correct or making some mistake?
Following is sample of my code
Controller:
public ActionResult AddCustomer(CustomerModel model)
{
if (ModelState.IsValid)
{
model.AddCustomer();
return RedirectToAction("Index", "Home");
}
return (View("AddCustomer",model));
}
Model:
public class AddBookModel
{
[Required(ErrorMessage = "The ISBN is required.")]
[DisplayName("ISBN")]
public String ISBN { get; set; }
[DisplayName("Title")]
[Required(ErrorMessage = "The Title is required.")]
public String Title { get; set; }
[Required(ErrorMessage = "The Publisher is required.")]
[DisplayName("Publisher")]
public String Publisher { get; set; }
public void AddBook()
{
using (BBBDataContext DCBook = new BBBDataContext())
{
Book tableBook = new Book()
{
ISBN = this.ISBN,
Title = this.Title,
Publisher = this.Publisher,
}
DCBook.Books.InsertOnSubmit(tableBook);
DCBook.SubmitChanges();
}
}
View:
<% using (Html.BeginForm()) {%>
<%= Html.ValidationSummary(true) %>
<fieldset>
<legend>Insert Book Record</legend>
<table id="displayform" cellspacing="0" cellpadding="5">
<colgroup>
<col span="1" style="text-align:right" />
<col span="2" style="text-align:left" />
</colgroup>
<tr>
<td class="editor-label">
<%= Html.LabelFor(model => model.ISBN) %>
</td>
<td class="editor-field">
<%= Html.TextBoxFor(model => model.ISBN) %>
<%= Html.ValidationMessageFor(model => model.ISBN) %>
</td>
</tr>
<tr>
<td class="editor-label">
<%= Html.LabelFor(model => model.Title) %>
</td>
<td class="editor-field">
<%= Html.TextBoxFor(model => model.Title) %>
<%= Html.ValidationMessageFor(model => model.Title) %>
</td>
</tr>

Your understanding of MVC is good.
The view only contains visual elements that will be displayed and doesn't contain any logic code.
The controller is listening to the view's events (mouseClick, lostfocus...), interact with the model, do verifications...
And the model contains your business class and interacts with databases and others external services.

Related

Get Value of Property (List<long>) in Post Action in ASP.NET MVC3

This is My model:
public class MyModel
{
public List<long> NeededIds { get; set; }
public string Name { get; set; }
}
My Controllers:
public ActionResult Create()
{
MyModel model = new MyModel();
model.NeededIds = new List<long> { 1, 2, 3, 4 };
return View(model);
}
[HttpPost]
public ActionResult Create(MyModel model)
{
string name = model.Name;
List<long> ids = model.NeededIds;
return RedirectToAction("Index");
}
And View:
#model TestMVC.Models.MyModel
#using(Html.BeginForm()) {
<table>
<thead>
<tr>
<th>
Id
</th>
</tr>
</thead>
<tbody>
#foreach(long id in Model.NeededIds) {
<tr>
<td>
#id
</td>
</tr>
}
</tbody>
</table>
#Html.ValidationSummary(true)
<fieldset>
<legend>MyModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
I set NeededIds in Get action and in the view I can see NeededIds. I also need it in Post action, but in post action the NeededIds is always null. How can I get the property value in post action when I set it in get action? What is your suggestion?
You are not posting your NeededIds back to the server. In order to get this working you can add them as hidden fields in a for loop inside the form:
#for (int i = 0; i < Model.NeededIds.Count(); i++) {
#Html.HiddenFor(model => model.NeededIds[i])
}
if you are using layout page than simply remove the form tag from the layout page.
in addition to the answer by Yakimych
you have kept the ids as constant.. this means two things
1. you can use arrays in place of list
2.you can just save the ids list/array in TempData and retrive it back from there when POST happens
you can do this like this
in your GET handler
TempData.Add("ids",idArray);
in your POST handler
var idArray = (long[])TempData["ids"];

Display name in Data Entity framework

I'd like to know how to change the display name of a model, and customize error messages in Entity Framework. I tried the following but it didn't work.
[Required(ErrorMessage = "Required .... :")]
[Display(Name = "Name Agency : ")]
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String Nag
{
get
{
//code
}
set
{
//code
}
}
This is the code behind my form that adds data into my database. I've omitted irrelevant lines.
<% using (Html.BeginForm("addcar", "Agence", FormMethod.Post, new { #class = "search_form" }))
{ %>
<%: Html.ValidationSummary(true) %>
<div class="editor-label">
<%: Html.LabelFor(model => model.Dmcv) %>
</div>
<div class="editor-field">
<%: Html.EditorFor(model => model.Dmcv) %>
<%: Html.ValidationMessageFor(model => model.Dmcv) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Puisv) %>
</div>
<div class="editor-field">
<%: Html.EditorFor(model => model.Puisv) %>
<%: Html.ValidationMessageFor(model => model.Puisv) %>
</div>
// Similaire code
<p>
<input type="submit" value="Create" />
</p>
<% } %>
Change [Display(Name = "Name Agency")] to [DisplayName("Name Agency")] instead.
First you need to reference this:
using System.ComponentModel.DataAnnotations;
For changing the display name of the column, actually [Display(Name="Name Agency")] is OK. I'm using it in my projects.
For error message
[Required(ErrorMessage="Required...")]
I read that it is possible that this won't work if you are using the entity framework designer because the designer overwrites your changes over and over then you will need to use the metadatatype something like this:
[MetadataType(typeof(MetadataMyClass))]
public partial class myclass
{
}
//data annotations here
public class MetadataMyClass
{
[Required(ErrorMessage = "Required...")]
[Display(Name="Column Name")]
public global:: System.String Nag
{
// ... etc, etc...
}
}

how to create multiple textbox with a list of objects

hi folks i have to create a share stuff page where i have a contentitem to share, the user who share it and the recipients of the ppl he wants to share it with, but i want to do it dynamic, this is my domain model
public class ShareContentItemModel : BaseModel
{
public ContentItem ContentItem { get; set; }
public User User { get; set;}
[Display(Name = "Recipients")]
public List<Recipient> Recipients { get { return new List<Recipient> { new Recipient { Name = "Recipient name here", Email = "Write Recipient Email here" } }; } }
[Required]
[Display(Name = "Nachricht")]
public string Message { get; set; }
}
public class Recipient{
[Display(Name = "Recipient Name:")]
public string Name { get; set;}
[Display(Name = "Recipient Email Address:")]
public string Email { get; set;}
}
and this is my view
#using (Html.BeginForm("Submit", "Contact", FormMethod.Post))
{
<p>Hi: #Html.DisplayTextFor(m=>m.User.Salutation) #Html.DisplayTextFor(m=>m.User.Firstname) #Html.DisplayTextFor(m=>m.User.Lastname)</p>
<table border="0" style="padding:5">
<tr>
<td class="editor-label">#Html.LabelFor(m => m.ContentItem.Title): </td>
<td class="editor-field">#Html.DisplayTextFor(m => m.ContentItem.Title)
<div>#Html.ValidationMessageFor(m => m.ContentItem.Title)</div>
</td>
</tr>
<tr>
<td class="editor-label">#Html.LabelFor(m => m.ContentItem.Description): </td>
<td class="editor-field">#Html.DisplayTextFor(m => m.ContentItem.Description)
<div>#Html.ValidationMessageFor(m => m.ContentItem.Title)</div>
</td>
</tr>
<tr><td colspan="2">Recipients:</td></tr>
#foreach (var item in #Model.Recipients)
{
<tr>
<td>#item.Name: #Html.TextBoxFor(/*Dont know what to put in here*/)</td>
<td>#item.Email: #Html.TextBoxFor(/*Dont know what to put in here*/) </td>
</tr>
}
<tr>
<td> <a class="small button" href="#">Add Recipient:</a> </td>
<td><input type="submit" class="button med primary" style="float: right;" value="ABSENDEN" /></td>
</tr>
</table>
}
as you can see in the //Dont know what to put in here
i cant use the item.name or item.email properties can you help me with this
p.d. the object is fine the cshtml is rendering fine, i just need to create this textboxes to start creating more recipients.
thank you very much
OK. So here is how you do it. You create an Editor template and use it.
Step 1) Create a folder called "EditorTemplates" in your View/yourViewFolderName
Step 2) Create a View called Receipent.cshtml
Add this code to that file
#model YourNameSpace.Models.Recipient
<p>
#Html.EditorFor(x=>x.Name) : #Html.TextBoxFor(x=>x.Name)
</p>
<p>
#Html.EditorFor(x=>x.Email) : #Html.TextBoxFor(x => x.Email)
</p>
Step 3) In your Main View, just call the editor template instead of the foreach loop code
#Html.EditorFor(x=>x.Recipients)
This should work fine. I tested with your models.
Keep your editor template name same as of your Property name which you want to show in a foreach. MVC will take care of the rest.

Asp.net MVC Razor more than one form on a page

Yo
I have a registration page on my site - at the top of the page is a login form for existing users. In the main area there is the registration form.
The login are is a partial view with #model ViewModels.LoginViewModel
The registration are is also a partial with #model ViewModels.RegViewModel
The main page which houses these partials is a view with #model ViewModels.RegPageViewModel
This viewmodel looks like:
public class RegViewModel
{
public RegisterVm RegisterVm { get; set; }
public LoginVm LoginVm { get; set; }
}
When I submit the registration part of the page (it's action is register/capture - the receiving action expects a RegisterVm) to it's controller it complains about being passed the wrong viewmodel
What's the deal with subviews and their viewmodel? Is there a standard approach to dealing with this?
Should I have one submit URL for this page which figures out if it's a login request or a register request and then handles the post accordingly? That seems messy to me though...
http://monobin.com/__d33cf45a4 - RegisterVm.cs (LoginVm.cs is pretty much the same as this)
http://monobin.com/__m69132f76 - RegPageVm.cs
Register.cshtml:
#model xxxx.ViewModels.RegPageVm
#{
View.Title = "Register";
Layout = "~/Views/Shared/_BareBones.cshtml";
}
<link rel="stylesheet" href="#Url.Content("~/Public/Css/signup.css")" type="text/css" />
<div id="sign-up-container">
<div id="sign-up-box">
<div id="sign-up-box-left">
<img src="#Url.Content("~/Public/Images/Signup_176x81.png")" />
</div>
<div id="sign-up-box-right">
#Html.Partial("_Register")
</div>
</div>
</div>
<div class="clear">
</div>
_Register.cshtml:
#model xxxx.ViewModels.RegisterVm
#using (Html.BeginForm("Capture", "Register", FormMethod.Post))
{
<table class="sign-up-box-inner">
<tr>
<td class="label-area">
#Html.LabelFor(x => x.Email)
</td>
<td class="field-area">
#Html.TextBoxFor(x => x.Email, new { #class = "login-input", title = "Enter Name" })
</td>
</tr>
<tr>
<td class="label-area">
#Html.LabelFor(x => x.Password)
</td>
<td class="field-area">
#Html.PasswordFor(x => x.Password, new { #class = "login-input", title = "Enter Name" })
</td>
</tr>
<tr>
<td class="label-area">
#Html.LabelFor(x => x.UserName)
</td>
<td class="field-area">
#Html.TextBoxFor(x => x.UserName, new { #class = "login-input", title = "Enter Name" })
</td>
</tr>
<tr>
<td colspan="2">
<input type="image" src="../../Public/Images/Submit_150x47.png" class="submit-button" />
</td>
</tr>
</table>
#Html.AntiForgeryToken()
}
And finally RegisterController.cs:
public class RegisterController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Capture(RegisterVm registerVm)
{
if (!ModelState.IsValid)
{
return View("index", new RegPageVm()
{
LoginVm = new LoginVm(),
RegisterVm = registerVm
});
}
return RedirectToAction("index", "Event");
}
}
w://
You need to ensure that the form elements (like the textbox etc) should have the same id as the RegisterVM and LoginVM properties. Your theory is right but I think you might be making a mistake in the naming convention of MVC.
If you can share your view code + the VM classes, then we'll be able to help better.
EDIT:
Looking at your code I think you should be passing the view model to your partial view. Like for example the following line believe should be like this >
#Html.Partial("_Register", Model.RegisterVm)
According to your answer to nEEbz:
You are using:
Html.TextBoxFor(x=>x.LoginVM.Email) // i guess
this would turn into <input name="LoginVM.Email" ...>
Notice the LoginVM. part
Your login action probably looks like:
public ActionResult Login(LoginVM model) { }
so it expect field names like Email and Password, not LoginVM.Email and LoginVM.Password.
So you could could use Html.Textbox instead (so that the field name doesn't get autocreated).

DropDownListFor not binding on Edit View with repeating items (List<T>)

Here is the thing. I have an Edit view, which doesnt bind the dropdowns' value when I open it.
[NonAction]
public List<SelectListItem> VraagType() {
List<SelectListItem> l = new List<SelectListItem>();
SelectListItem a = new SelectListItem();
SelectListItem b = new SelectListItem();
a.Text = "Meerkeuze";
a.Value = "M";
b.Text = "Open";
b.Value = "O";
l.Add(a);
l.Add(b);
return l;
}
[NonAction]
public List<SelectListItem> getSchalen() {
return _db.EvalSchaals.ToList().ToSelectList(q => q.Sch_Naam, q => q.Sch_ID.ToString(), q => q.Sch_ID == -1).ToList();
}
public ActionResult Edit(int id) {
ViewData["vraagtype"] = VraagType();
ViewData["schaal"] = getSchalen();
EvalVragenBlok evb = _db.EvalVragenBloks.First(q => q.Vrbl_ID == id);
List<EvalVragen> ev = _db.EvalVragens.Where(q => q.Vrbl_ID == id).ToList();
FlatEvalVragenBlok fevb = Mapper.Map<EvalVragenBlok, FlatEvalVragenBlok>(evb);
fevb.Vragen = new List<FlatEvalVragen>();
return View(fevb);
}
this is the code from the controller.
here is the code from the Edit.aspx view
<h2>
Edit</h2>
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.Vrbl_Titel) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Vrbl_Titel) %>
<%: Html.ValidationMessageFor(model => model.Vrbl_Titel) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Sch_ID) %>
</div>
<div class="editor-field">
<%: Html.DropDownListFor(model => model.Sch_ID, ViewData["schaal"] as List<SelectListItem>, "Selecteer een schaal...") %>
<%: Html.ValidationMessageFor(model => model.Sch_ID) %>
</div>
<%= Html.ValidationMessageFor(model => model.Vragen) %>
<table id="vragentbl">
<tr>
<th>
</th>
<th>
Vraag
</th>
<th>
Soort
</th>
</tr>
<% if (Model.Vragen != null) { %>
<% for (int i = 0; i < Model.Vragen.Count; i++) { %>
<tr>
<td>
<%=i + 1%>
</td>
<td>
<%= Html.TextBoxFor(model => model.Vragen[i].Evvr_Vraag, new { style = "width:400px" })%><br />
<%= Html.ValidationMessageFor(model => model.Vragen[i].Evvr_Vraag)%>
</td>
<td>
<%= Html.DropDownListFor(model => model.Vragen[i].Evvr_Type, ViewData["vraagtype"] as List<SelectListItem>, new { style = "width:95px" })%><br />
<%= Html.ValidationMessageFor(model => model.Vragen[i].Evvr_Type)%>
</td>
</tr>
<% }
} %>
<tr>
<td>
</td>
<td>
<a id="addnew" href="#">Voeg extra keuze toe</a>
</td>
<td>
</td>
</tr>
</table>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
I have 2 List 's. 1 of them is in the non-repeating part of the form (Schalen), the other one (VraagType) is Inside the repeating part.
for Schalen, everything works fine. i open the edit view, and all fields are filled in like it should be. the Vrbl_Titel has its value, and the dropdown of Sch_ID has the value it received from the object which i sent with the view, which came from the DB.
The problem lies in the repeating part.
the textbox for model.Vragen[i].Evvr_Vraag get's its value, and the dropdown for model.Vragen[i].Evvr_Type is shown, however, this dropdown does not get the value which was sent in the object. it keeps it's default standard value, which is the first item in the 'selectlist'
how do i get my value from my 'Vragen' object, into the dropdown. if i put the value in a simple textbox
<%= Html.TextBoxFor(model => model.Vragen[i].Evvr_Type)%>
then the textbox does get the value. so the problem is that the dropdownvalue doesnt change form it's initial value... bug in MVC?
just for info, this is how the object(s) look sent to the view:
namespace MVC2_NASTEST.Models {
public partial class FlatEvalVragenBlok {
public int Vrbl_ID { get; set; }
public int Sch_ID { get; set; }
public string Vrbl_Titel { get; set; }
public List<FlatEvalVragen> Vragen { get; set; }
}
}
namespace MVC2_NASTEST.Models {
public partial class FlatEvalVragen {
public int Evvr_ID { get; set; }
public int Vrbl_ID { get; set; }
public int Evvr_rang { get; set; }
public string Evvr_Vraag { get; set; }
public char Evvr_Type { get; set; }
}
}
It seems this is really a bug or at least inconsistency in ASP.NET MVC 2. I have examined its source and found what InputHelper() method called from TextBoxFor() helper receives default value calculated with
ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model
But SelectInternal() method called from DropDownListFor() helper receives only a name of a control found with ExpressionHelper.GetExpressionText() method.
So SelectInternal() tries to find default value using ViewData.Eval() method from MVC 1. It's known what this method isn't able to extract values from arrays by numeric index.
So in your case are applicable
<%: Html.DropDownListFor(model => model.Sch_ID) %>
<%= Html.TextBoxFor(model => model.Vragen[i].Evvr_Type)%>
but not
<%: Html.DropDownListFor(model => model.Vragen[i].Evvr_Type) %>
because it's equivalent to
<%: Html.DropDownList("Vragen[" + i + "].Evvr_Type") %>
At the same time I want to emphasize again what
<%= Html.TextBoxFor(model => model.Vragen[i].Evvr_Type)%>
isn't equivalent to
<%= Html.TextBox("model.Vragen[" + i + "].Evvr_Type")%>
because latter even in MVC 2 can't bind default value.
Possible workarounds
First. Since SelectInternal() also checks ModelState dictionary you can fill this dictionary before returning the view.
for (int i=0; i < fevb.Vragen.Count(); i++)
ModelState.Add("Vragen[" + i + "].Evvr_Type", new ModelState
{
Value = new ValueProviderResult(fevb.Vragen[i].Evvr_Type, null,
CultureInfo.CurrentCulture)
});
This will be done by MVC itself after from post, so you should do it manually only first time.
Second. Instead of
<%= Html.DropDownListFor(model => model.Vragen[i].Evvr_Type,
ViewData["vraagtype"] as List<SelectListItem>)%>
use
<%= Html.DropDownListFor(model => model.Vragen[i].Evvr_Type,
new SelectList(ViewData["vraagtype"] as IEnumerable, "Value", "Text",
Model.Vragen[i].Evvr_Type))%>
ViewData["vraagtype"] in this case doesn't have to contain objects of SelectListItem, any IEnumerable is enough. You may check SelectList() method description in case of need.

Resources