I have a problem on passing values from view to controller
Here is my view:
#model IEnumerable<SQLOperation.Models.QuestionClass.Tabelfields>
#{
ViewBag.Title = "Question";
}
<h3> Question</h3>
#{int i = 0;}
#foreach (var item in Model)
{
using (Html.BeginForm("Question", "Home"))
{
#Html.DisplayFor(modelItem => item.QuestionName)
#Html.HiddenFor(m => item.QuestionID)
<br /><br />
if (item.Option1 != "")
{
#Html.RadioButtonFor(m => item.SelectedOption, item.Option1, item)
#Html.DisplayFor(modelItem => item.Option1)
<br /><br />
}
if (item.Option2 != "")
{
#Html.RadioButtonFor(m => item.SelectedOption, item.Option2, item)
#Html.DisplayFor(modelItem => item.Option2)
<br /><br />
}
if (item.Option3 != "")
{
#Html.RadioButtonFor(m => item.SelectedOption, item.Option3, item)
#Html.DisplayFor(modelItem => item.Option3)
<br /><br />
}
if (item.Option4 != "")
{
#Html.RadioButtonFor(m => item.SelectedOption, item.Option4, item)
#Html.DisplayFor(modelItem => item.Option4)
<br /><br />
}
i = (Int16)i + 1;
if (Model.Count() == i)
{
<input name="btnsumbit" type="submit" value="Submit Feedback"
style="font-family:Segoe UI Light;font-size:medium;"/>
}
}
}
My controller :
[HttpGet]
public ActionResult Question(string email)
{
var tf = new QuestionClass.Tabelfields();
IList<QuestionClass.Tabelfields> viewmodel = new List<QuestionClass.Tabelfields>();
var q = QuestionClass.getallQuestion(email).ToList();
foreach (SQLOperation.Models.Question item in q)
{
QuestionClass.Tabelfields viewItem = new QuestionClass.Tabelfields();
viewItem.Email = item.Email;
viewItem.QuestionID = item.QuestionID;
viewItem.QuestionName = item.QuestionName;
viewItem.Option1 = item.Option1;
viewItem.Option2 = item.Option2;
viewItem.Option3 = item.Option3;
viewItem.Option4 = item.Option4;
viewmodel.Add(viewItem);
}
return View(viewmodel);
}
[HttpPost, ActionName("Question")]
public void Question(IEnumerable<QuestionClass.Tabelfields> items)
{
}
My Model:
public class QuestionClass
{
public static FeedbackDatabaseDataContext context = new FeedbackDatabaseDataContext();
public class Tabelfields : Question
{
//public decimal QuestionID { get; set; }
//public string Email { get; set; }
//public string QuestionName { get; set; }
//public string Option1 { get; set; }
//public string Option2 { get; set; }
//public string Option3 { get; set; }
//public string Option4 { get; set; }
public string SelectedOption { get; set; }
}
public static List<Question> getallQuestion(string email)
{
var list = (from q in context.Questions where q.Email == #email select q);
return list.ToList();
}
}
however I get NULL in "items" in controller.
[HttpPost, ActionName("Question")]
public void Question(IEnumerable<QuestionClass.Tabelfields> items)
{
}
Whereas if I change my View & Controller to below , I get last value from database in controller
View:
#foreach (var item in Model)
{
using (Html.BeginForm("Question", "home", new { email=item.Email, q=item.QuestionID}))
{
#Html.DisplayFor(modelItem => item.QuestionName)
#Html.HiddenFor(m => item.QuestionID)
.
.
.
.
}
}
Controller:
[HttpPost, ActionName("Question")]
public void Question(string email,int q)
{
}
I get values in email and q
so how can I get all values i.e. QuestionId,Email,Questionname and it's appropriate selected value (radiobutton) in controller ?
i.e. in Following Controller:
[HttpPost, ActionName("Question")]
public void Question(IEnumerable<QuestionClass.Tabelfields> items)
{
}
You need to index the Html.*For items as such;
#Html.RadioButtonFor(m => m[i].SelectedOption, item.Option3, item)
To make things simplier, i'd probably get rid of the foreach & and separate i declaration and use the following;
#for(int i=0; i < Model.Count; i++)
{
#Html.HiddenFor(m => m[i].QuestionID)
#Html.RadioButtonFor(m => m[i].SelectedOption, Model[i].Option3, Model[i])
}
etc.
Indexing like this will cause the html to be rendered with the indexing intact:
<input type='hidden' name=[0].'QuestionId' />
<input type='hidden' name=[1].'QuestionId' />
<input type='hidden' name=[2].'QuestionId' />
Rather than what you're doing currently, which ends up rendering as so;
<input type='hidden' name='QuestionId' />
<input type='hidden' name='QuestionId' />
<input type='hidden' name='QuestionId' />
Without the indexing, each form field is given the same name, so you're controller is going to think only one was returned.
Related
I don't want to check StudentID. I just want only StudentName validation. But when I am running the code it is showing me the validation of both properties.How can I solve this problem
public class Student
{
public int StudentID { get; set; }
[Required]
public string StudentName { get; set; }
}
Controller Code:
[HttpPost]
public ActionResult Crud(Student student)
{
if(ModelState.IsValid)
{
return RedirectToAction("Crud");
}
return View();
}
View Page Code:
#using (Ajax.BeginForm("Crud", "Students", new AjaxOptions
{ HttpMethod = "POST" }))
{
#Html.ValidationSummary()
<div>
#Html.LabelFor(x => x.StudentID)<br />
#Html.EditorFor(x => x.StudentID)
</div><br />
<div>
#Html.LabelFor(x => x.StudentName)<br />
#Html.EditorFor(x => x.StudentName)
</div><br />
<input type="submit" value="SAVE" />
}
Output:
Both validation are showing here. But I want only StudentName validation
ModelState.IsValid is always returning true.
Code:
user.cs
public class User
{
public int UserID { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
HomeController.cs
public ActionResult SaveUser(User user)
{
if (ModelState.IsValid)
{
//create DBContext object
using (var dbCtx = new UsersDbEntities())
{
dbCtx.Entry(user).State = EntityState.Modified;
dbCtx.SaveChanges();
}
return View("ShowUser", user);
}
return View("EditUser", user);
}
Register.cshtml
#model TrainingWebsite.Models.User
<div id="myForm">
#using (Html.BeginForm("RegisterUser", "Home", FormMethod.Post))
{
if (#ViewBag.Message != null)
{
<div style="border: 1px solid red">
#ViewBag.Message
</div>
}
<table>
<tr>
<td>#Html.LabelFor(a => a.Username)</td>
<td>#Html.TextBoxFor(a => a.Username, new { id = "id_username" })</td>
<td>#Html.ValidationMessageFor(a => a.Username)</td>
</tr>
<tr>
<td>#Html.LabelFor(a => a.FirstName)</td>
<td>#Html.TextBoxFor(a => a.FirstName, new { id = "id_firstName" })</td>
<td>#Html.ValidationMessageFor(a => a.FirstName)</td>
</tr>
<tr>
<td>#Html.LabelFor(a => a.LastName)</td>
<td>#Html.TextBoxFor(a => a.LastName, new { id = "id_lastName" })</td>
<td>#Html.ValidationMessageFor(a => a.LastName)</td>
</tr>
</table>
ModelState.IsValid is true even though Last Name field is empty yet is a required field
Any help would be appreciated. Thanks in advance
David
I would like to add a textbox (date) and button to my report, which filters the data.
The below mvc is working, but the input must still be validated (must be a DATE) on client side (and server side if possible)
My Model looks like this :
public class DailyReport
{
public int DailyReportID { get; set; }
public DateTime? ReportDate { get; set; }
}
View :
#model IEnumerable<project_name.Models.DailyReport>
#* text box and button: *#
#using (Html.BeginForm("Index", "DailyReport", FormMethod.Get))
{ <p>
Title: #Html.TextBox("SearchDateString")
<input type="submit" value="Filter" />
</p>
}
#* display dates*#
#foreach (var item in Model)
{ #Html.DisplayFor(modelItem => item.ReportDate)
}
my controller:
public ViewResult Index(String SearchDateString)
{
var dailyreport = db.DailyReport.Include(d => d.Site);
if (!String.IsNullOrEmpty(SearchDateString))
{
DateTime search_date = Convert.ToDateTime(SearchDateString);
dailyreport = dailyreport.Where(r => r.ReportDate == search_date);
}
return View(dailyreport.ToList());
}
Can someone help me please?
How do I make sure a valid date is entered in the textbox?
Should I create a another model with a date field for this input?
Utilize the DataTypeAttribute from the DataAnnotations namespace in your Model, like so:
public class DailyReport
{
public int DailyReportID { get; set; }
public DateTime? ReportDate { get; set; }
}
public class DrViewModel
{
[DataType(DataType.Date)]
public string DateTimeSearch { get; set; }
List<DailyReport> DailyReports { get; set; }
}
In your View, have something like:
#model project_name.Models.DrViewModel
#using (Html.BeginForm("Index", "DailyReport", FormMethod.Get))
{
<p>
Title: #Html.TextBoxFor(m => m.DateTimeSearch)
<input type="submit" value="Filter" />
</p>
}
#foreach (var item in Model.DailyReports)
{
#Html.DisplayFor(m => item.ReportDate)
}
#Shark Shark pointed me in the right direction to use a viewmodel, this is the end result that is now working. JS validation added as well.
(DBSet was not necessary because DrViewModel is a viewmodel.)
controllers :
public ActionResult Index(DrViewModel dvm)
{
var dailyreport = db.DailyReport.Include(d => d.Site);
if (dvm.DateTimeSearch != null)
{
dailyreport = dailyreport.Where(r => r.ReportDate == dvm.DateTimeSearch);
}
dvm.DailyReport = dailyreport.ToList();
return View(dvm);
}
models :
public class DrViewModel
{
public DateTime? DateTimeSearch { get; set; }
public List<DailyReport> DailyReport { get; set; }
}
public class DailyReport
{
public int DailyReportID { get; set; }
public DateTime? ReportDate { get; set; }
}
view :
#model myproject.Models.DrViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm("Index","DailyReport", FormMethod.Get ))
{
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.DateTimeSearch)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DateTimeSearch)
#Html.ValidationMessageFor(model => model.DateTimeSearch)
<input type="submit" value="Filter" />
</div>
}
#foreach (var item in Model.DailyReport)
{
#Html.DisplayFor(modelItem => item.ReportDate)
}
My question isn't so much about displaying the data its about collecting changes to the data.
My specific scenario is the need to allow users to delete multiple items from a list. I don't know if i'm even approaching this in a locgical way.
The List is a collection of Private Messages. My view model has strings for To, From, Subject, and a bool for "Delete".
public class PrivateMessagesModel {
public PrivateMessagesModel()
{
PrivateMessages = new List<PrivateMessageReceivedModel>();
}
public List<PrivateMessageReceivedModel> PrivateMessages;
}
public class PrivateMessageReceivedModel
{
[DataType(DataType.Text)]
[Display(Name = "From")]
public string From { get; set; }
[DataType(DataType.Text)]
[Display(Name = "Subject")]
public string Subject { get; set; }
[DataType(DataType.Text)]
[Display(Name = "Message")]
public string Message { get; set; }
[DataType(DataType.Text)]
[Display(Name = "Date")]
public DateTime DateTimeSent { get; set; }
[DataType(DataType.Text)]
[Display(Name = "Delete")]
public bool Delete { get; set; }
}
The code to display looks like this. And works ok.
#
model ScaleRailsOnline.Models.PrivateMessagesModel
#{
ViewBag.Title = "Private Messages";
}
<div id="content">
<div class="content">
<h2>
Private Messages</h2>
#using (Html.BeginForm())
{ <table>
#for (int i = 0; i < Model.PrivateMessages.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(m => m.PrivateMessages[i].Delete)
</td>
<td>
#Html.DisplayTextFor(m => m.PrivateMessages[i].From)
</td>
</tr>
}
</table>
<p>
<input type="submit" value="Delete" />
</p>
}
</div>
</div>
The problem is when I check a couple of the check boxes and hit the delete button, i get nothing back in the model.
Again, im sure i'm not approaching this in the right way. Any help would be appreciated.
You need to have a controller associated to the Html.BeginForm where the form will be posted.
eg
<div id="formid">
#using (Html.BeginForm("Index1", "Home", new AjaxOptions { UpdateTargetId = "formid",OnSuccess ="OnSuccess" }, new { id = "TheForm" }))
{
#for (int i = 0; i < Model.PrivateMessages.Count; i++)
{
#Html.CheckBoxFor(m => m.PrivateMessages[i].Delete)
}
<input type="submit" name="name" value="Submit" />
}
</div>
On Controller
public ActionResult Index1(List<PrivateMessageReceivedModel> mod)
{
//Now in the mod you will get the value of the delete checkbox.
}
You have to create a Controller class for your model. Then create method that return ActionResult type. and then create View of the newly created controller method.
Here is a sample code.
Model Class
namespace Mvc3Application1.Models
{
public class PrivateMessage
{
public string From { get; set; }
public string Subject { get; set; }
public string Message { get; set; }
public DateTime DateTimeSent { get; set; }
public bool Delete { get; set; }
}
public class PrivateMessageRepository
{
public List<PrivateMessage> GetPrivateMessages()
{
List<PrivateMessage> myPrivateMessages=new List<PrivateMessage>();
//add list of messages in the object myPrivateMessages
myPrivateMessages.Add(new PrivateMessage { From = "abc#abc.com", Subject = "Subject 1", Message = "Message 1", DateTimeSent = DateTime.Now, Delete = false });
myPrivateMessages.Add(new PrivateMessage { From = "abc1#abc.com", Subject = "Subject 2", Message = "Message 2", DateTimeSent = DateTime.Now, Delete = false });
myPrivateMessages.Add(new PrivateMessage { From = "abc2#abc.com", Subject = "Subject 3", Message = "Message 3", DateTimeSent = DateTime.Now, Delete = false });
myPrivateMessages.Add(new PrivateMessage { From = "abc3#abc.com", Subject = "Subject 4", Message = "Message 4", DateTimeSent = DateTime.Now, Delete = false });
myPrivateMessages.Add(new PrivateMessage { From = "abc4#abc.com", Subject = "Subject 5", Message = "Message 5", DateTimeSent = DateTime.Now, Delete = false });
return myPrivateMessages;
}
}
}
Controller class
namespace Mvc3Application1.Controllers {
public class PrivateMessageController : Controller
{
//
// GET: /PrivateMessage/
// Show all the private messages
public ActionResult ListMessages()
{
return View(new Models.PrivateMessageRepository().GetPrivateMessages());
}
//delete the selected messages and return the collection
[HttpPost]
public ActionResult ListMessages(FormCollection collection)
{
List<Models.PrivateMessage> messages = new Models.PrivateMessageRepository().GetPrivateMessages();//all messages
List<Models.PrivateMessage> cloneMessages = new List<Models.PrivateMessage>();//messages left for deletion
int noOfItems = 0;//no of items deleted
int currentItem = 0;//current item in collection 'messages'
string[] deletedItems = collection[0].Split(',');//return from HTML control collection
for (int i = 0; i < deletedItems.Length; i++)
{
if (bool.Parse(deletedItems[i]) == false)//if checkbox is unchecked
{
cloneMessages.Add(messages[currentItem]);//copy original message in cloneMessages collection
}
else //if checkbox is checked
{
noOfItems++;
i++;
}
currentItem++;
}
ViewBag.Items = noOfItems + " message(s) deleted.";
return View(cloneMessages);
}
} }
View
#model IEnumerable<Mvc3Application1.Models.PrivateMessage>
#{
ViewBag.Title = "Private Messages";
}
<h2>Private Messages</h2>
<div style="color:green;font-weight:bold;">#ViewBag.Items</div>
<br />
#using (Html.BeginForm())
{
<table>
<tr>
<th></th>
<th>
From
</th>
<th>
Subject
</th>
<th>
Message
</th>
<th>
DateTimeSent
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.CheckBoxFor(modelItem => item.Delete)
</td>
<td>
#Html.DisplayTextFor(modelItem => item.From)
</td>
<td>
#Html.DisplayTextFor(modelItem => item.Subject)
</td>
<td>
#Html.DisplayTextFor(modelItem => item.Message)
</td>
<td>
#Html.DisplayTextFor(modelItem => item.DateTimeSent)
</td>
</tr>
}
</table>
<input type="submit" value="Delete"/>
}
I have a List<Task> in my model. This list contains 2 tasks(say) in it
public List<Task> Tasks { get; set; }
public class Task {
public Task()
{
Title="";
Total= 0;
}
public string Title{ get; set; }
public int Total{ get; set; }
}
Now in my razor view, I want render 2 text boxes for each of the Tasks in the List<Tasks> of my model.
I didn't loop, just placed direct text boxes like:
#Html.TextBoxFor(m => m.Tasks[0].Title, new { #maxlength = "50"})
#Html.TextBoxFor(m => m.Tasks[0].Total, new { #maxlength = "2"})
<br>
#Html.TextBoxFor(m => m.Tasks[1].Title, new { #maxlength = "50"})
#Html.TextBoxFor(m => m.Tasks[1].Total, new { #maxlength = "2"})
This renders the form fine, but clicking the submit button doesn't do anything in FF.
However it posts fine in IE9.
View source shows this html generated like this:
<input id="Tasks_0__Title" maxlength="50" name="Tasks[0].Title" type="text" value="" />
<input data-val="true" data-val-number="The field Total must be a number." data-val-required="The Total field is required." id="Tasks_0__Total" maxlength="2" name="Tasks[0].Total" type="text" value="" />
This HTML doesn't look right. It has name="Tasks[0].Total" which seems odd.
How should I do this so that I can access the text box values from List<> in my controller after post?
Thanks
EDIT:
I just kept one row for test. This is the html I see in FF.
<input id="Tasks_0__Title" type="text" value="" name="Tasks[0].Title" maxlength="50">
<input id="Tasks_0__Total" type="text" value="" name="Tasks[0].Total" maxlength="2" data-val-required="The Total field is required." data-val-number="The field Total must be a number." data-val="true">
This doesn't post when I click the submit button.
Now if I change name="Tasks[0].Title" to name="Tasks_0__Title" and name="Tasks_0__Total"
in FIREBUG it posts fine.
If I delete name completely it also posts fine in FF
You should use Tasks[0].Total and Tasks[1].Total instead of Items:
#Html.TextBoxFor(m => m.Tasks[0].Title, new { #maxlength = "50"})
#Html.TextBoxFor(m => m.Tasks[0].Total, new { #maxlength = "2"})
<br/>
#Html.TextBoxFor(m => m.Tasks[1].Title, new { #maxlength = "50"})
#Html.TextBoxFor(m => m.Tasks[1].Total, new { #maxlength = "2"})
name="Tasks[0].Total" is not odd. That's exactly how the input should be named in order for the model binder to fetch the value back in the POST action. See this blog post for the wire format used by the default model binder when dealing with lists and dictionaries.
This being said I would recommend you using editor templates => instead of writing those 5 lines of code in your view replace them with:
#Html.EditorFor(x => x.Tasks)
and then inside the corresponding editor template (~/View/Shared/EditorTemplates/Task.cshtml) which will be automatically rendered for each element in the Tasks collection:
#model Task
#Html.TextBoxFor(m => m.Title, new { #maxlength = "50"})
#Html.TextBoxFor(m => m.Total, new { #maxlength = "2"})
<br/>
Now you can leave the editor templates worry about proper naming convention, etc...
As far as your POST action is concerned:
[HttpPost]
public ActionResult Foo(MyViewModel model)
{
// model.Tasks should be correctly bound here
...
}
In case you want to have multiple elements with textboxes.
for (int i = 0; i < Model.Students.Count; i++)
{
#Html.HiddenFor(modelIem => Model.Students[i].StudentId)
<tr>
<td>
#Html.DisplayFor(modelItem => Model.Students[i].Student.FirstNames)
</td>
<td>
#Html.DisplayFor(modelItem => Model.Students[i].Student.LastNames)
</td>
<td>
#Html.TextBoxFor(modelItem => Model.Students[i].Score, new { #type = "number" })
</td>
</tr> }
This is the ViewModel
public class MyModel
{
public List<StudentGrade> Students { get; set; }
}
public class StudentGrade {
public ApplicationUser Student { get; set; }
[Range(1, 100)]
public int? Score { get; set; } = null;
public string Description { get; set; } = null;
public string StudentId { get; set; }
}
At the End it will look like this.