jQuery AutoComplete text not saving to db via create view using MVC3 - c#

This is my first post so please go easy on me fellas. I am trying to implement a create form that utilizes jquery autocomplete. The create form allows users to enter data that will be saved to my database, via a submit button. Here is my code:
Controller
// GET: /Inspection1/Create
public ActionResult Create()
{
InspectionInfo model = new InspectionInfo
{
Submitted = DateTime.Now,
Contact = new Contact()
};
ViewBag.CountyName = new SelectList(db.Counties, "CountyName", "CountyName");
return View(model);
}
//
// POST: /Inspection1/Create
[HttpPost]
public ActionResult Create(InspectionInfo inspectioninfo)
{
if (ModelState.IsValid)
{
db.InspectionInfos.Add(inspectioninfo);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(inspectioninfo);
}
// this allows for autocompletion behavior
public ActionResult QuickSearchContact(string term)
{
var contacts = db.Contacts
.Where(r => r.ContactName.Contains(term))
.Take(10)
.Select(r => new { label = r.ContactName });
return Json(contacts, JsonRequestBehavior.AllowGet);
}
Models
public class InspectionInfo
{
[Key]
public int InspectionId { get; set; }
[DataType(DataType.Date)]
public virtual DateTime Submitted { get; set; }
[DataType(DataType.MultilineText)]
[MaxLength(1000)]
public string Comments { get; set; }
[Required]
public Contact Contact { get; set; }
public class Contact
{
[Key]
public string ContactName { get; set; }
View:
<div class="editor-label">
#Html.LabelFor(model => model.Contact)
</div>
<div class="editor-field">
<input type ="text" name ="q" data-autocomplete=
"#Url.Action("QuickSearchContact", "Inspection")"/>
#Html.ValidationMessageFor(model => model.Contact.ContactName)
</div>
JS
$(document).ready(function () {
$(":input[data-autocomplete]").each(function () {
$(this).autocomplete({ source: $(this).attr("data-autocomplete")});
});
The autocomplete function seems to be working fine. It will pull column data from the database as I require. However, any data entered in the autocomplete text box, appears NULL in the database after the user has saved the form. Help here would be greatly appreciated.

For model binding to work, generally input names must match property names of your model. Surprisingly, you have named your input "q"
<input type ="text" name ="q" data-autocomplete="..."/>
Just rename it according to your model
<input type ="text" name="Contact.ContactName" data-autocomplete="..."/>

You don't have your on the code above but, instead of using
<input type ="text" name ="q" data-autocomplete= "#Url.Action("QuickSearchContact", "Inspection")"/>
use:
#EditorFor(x = x.NameOfTextBox)
then either have an input button wrapped in a using tag
#using (Html.BeginForm("Create", "NameOfController", FormMethod.Post){
//all your model stuff goes here
}
or use and actionlink instead of :
#Html.ActionLink("Submit", "Create", "NameOfController", Model)

The provided information doesn't tell, but is is likely that the autocomplete part is not written within the form elements of the view:
#using (Html.BeginForm())
{
<p>
...
</p>
}
In MVC the form is defined within the brackets { .... } like above.

Related

How to bind data from different SQL tables using dropdown?

I am trying to create an ASP.NET Core 3.1 MVC web app using identity. The app has users and admin. The admin can create new tasks and assign them to the users.
I get the list of users and tasks in 2 different dropdowns as follows:
View: Assign.cshtml
#model TaskManager2.ViewModels.AssignViewModel
#{
ViewData["Title"] = "Assign";
}
<h1>Assign</h1>
<h4>Assign</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Assign">
<div class="form-group">
#Html.DropDownListFor(t => t.TaskId,
Model.Tasks, "--Select task--")
</div>
<div class="form-group">
#Html.DropDownListFor(u => u.Id,
Model.Users, "--Select user--")
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
AdminController:
private readonly ApplicationDbContext _context;
public AdminController(ApplicationDbContext context)
{
_context = context;
}
public IActionResult Index()
{
return View();
}
public IActionResult Assign()
{
var users = (from u in _context.Users select new SelectListItem { Value = u.Id, Text = u.FirstName }).ToList();
var tasks = (from t in _context.Task select new SelectListItem { Value = t.TaskId.ToString(), Text = t.Description }).ToList();
var user = _context.Users.FirstOrDefault();
return View(new AssignViewModel { Users = users, Tasks = tasks });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Assign([Bind("TaskId, Id")] Assign assign)
{
if (ModelState.IsValid)
{
_context.Add(assign);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(assign);
}
ViewModel :
public class AssignViewModel
{
public IList<SelectListItem> Tasks;
public IList<SelectListItem> Users;
//public long SelectedUserId { get; set; }
//public long SelectedTaskId { get; set; }
//Added these lines instead
public Task TaskId { get; set; }
public IdentityUser Id { get; set; }
}
Model: Assign.cs
public partial class Assign
{
public long AssignId { get; set; }
public long TaskId { get; set; }
//[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
}
I got an error that is asked previously on stackoverflow: here and I tried the solution. I added the commented line on Assign.cs but the result that I get is not the one that I want. The idea is to save the Id of the user from AspNetUser and taskId from Task table in a third table called Assign. Here's how it looks like:
I am new to this, so I don't really understand how it works. I would really appreciate your help! Thank you!
Edit
I made the changes to the code above and also set Identity Specification to Yes for the primary key. It works and records the user Id and the corresponding taskId to the database. Now I'm trying to create something like:
#if ((bool)ViewData["HasError"]) //not working
{ <div class="alert alert-danger" role="alert">
Please select!
</div>
}
This will give an alert if the user doesn't select any of the options instead of throwing this error:
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'TaskManager2.Models.Assign', but this ViewDataDictionary instance requires a model item of type 'TaskManager2.ViewModels.AssignViewModel'.
In the http post line, you are sending Assign (entity model)
To the controller, so the model state would be done on Assign class,
I think you should be using AssignViewModel there, because all the interaction with views should happen with viewmodel, if your view model is valid then covert to actual entity.

MVC 3: DropDownList on an Edit Form for an object that is a property of a ViewModel

Overview:
I'm trying to use a ViewModel that has a property that is the model class that I'm trying to edit. I've seen the edit form work using the MVC scaffolding edit forms when editing a model directly, however I am trying to use a ViewModel that contains the model being edited. Everything works except for the saving of a field that's displayed in a DropDownList.
Explanation:
I've attempted to use the scaffolding features of MVC 3 to create an edit form for a model.
In the MVC Music Store tutorial, this is done for the Edit page of an Album, in the StoreManagerController.
Within that page, they have two drop downs for Genre and Artist. Each looks similar to this in the view:-
<div class="editor-label">
#Html.LabelFor(model => model.GenreId, "Genre")
</div>
<div class="editor-field">
#Html.DropDownList("GenreId", String.Empty)
#Html.ValidationMessageFor(model => model.GenreId)
</div>
As far as I can tell, these have their options filled out in the controller using the ViewBag.
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
Working with the Model directly
In my code, I managed to do the same with an object that's being saved to the DB through Entity Framework.
Model
public class Season
{
public int SeasonId { get; set; }
public int ClubId { get; set; }
public Club Club { get; set; }
}
Code in Controller
ViewBag.ClubId = new SelectList(clubs, "ClubId", "Name", season.ClubId);
View
<div class="editor-label">
#Html.LabelFor(model => model.ClubId, "Club")
</div>
<div class="editor-field">
#Html.DropDownList("ClubId", String.Empty)
#Html.ValidationMessageFor(model => model.ClubId)
</div>
This works fine.
Working with the View Model
However now I have realised that the page needs more text displayed than what is available in the model I'm editing.
I was hoping to create a special View Model, with the edited model (season) and the extra text I want to display.
ViewModel
public class EditSeasonViewModel
{
public string PlayerName {get; set;}
public string SummaryText {get; set;}
public int PlayerId {get; set;}
public Season season {get; set;}
}
I did this, changed the controller to have the HttpGet and HttpPost methods use the new ViewModel, changed the View to accept the new ViewModel, and changed the all 'EditorFor' methods in the view to use model.season.MyProperty.
Code in Controller
ViewBag.ClubId = new SelectList(clubs, "ClubId", "Name", seasonVM.season.ClubId);
Code in View
<div class="editor-label">
#Html.LabelFor(model => model.season.ClubId, "Club")
</div>
<div class="editor-field">
#Html.DropDownList("ClubId", String.Empty)
#Html.ValidationMessageFor(model => model.season.ClubId)
</div>
When debugging the HttpPost method, all of the values for season properly exist, except for the ClubId value which should come from the DropDropList.
I haven't changed the DropDownList in the View at all from the way it was when we were using the Model directly.
Question:
My question is, what do I need to change to get the ClubId to be saved properly when using this new EditSeasonViewModel?
Also, how does the ViewBag.ClubId in the HttpGet method in the controller match to the DropDownList in the View, and then have it's value passed back to the HttpPost method?
It's your DropDownList declaration that's incorrect. Try this instead:
#Html.DropDownListFor(m => m.season.ClubId, ViewBag.ClubId)
But really, you should put that select list in your model:
public SelectList Clubs { get; set; }
Then populate it in your controller:
Model.Clubs = new SelectList(clubs, "ClubId", "Name", season.ClubId);
Then you can do:
#Html.DropDownListFor(m => m.season.ClubId, Model.Clubs)
Avoid using dynamic stuff like ViewBag/ViewData to transfer data from action methods to views. Switch to the strongly typed approach.
Update your Viewmodel to have 2 more properties. one to hold a collection of available clubs and one to hold the selected club.
public class EditSeasonViewModel
{
public List<SelectListItem> Clubs { set;get;}
public int SelectedClub { set;get;}
public string PlayerName {get; set;}
public string SummaryText {get; set;}
public int PlayerId {get; set;}
public Season season {get; set;}
public EditSeasonViewModel()
{
Clubs=new List<SelectListItem>();
}
}
Now in your GET action, Get the clubs and load it to the Clubs Property.
public ActionResult create(int id)
{
var vm=new EditSeasonViewModel();
vm.Clubs=GetListOfSelectListItemFromClubsFromSomeWhere();
return View(vm);
}
assuming GetListOfSelectListItemFromClubsFromSomeWhere is a method which returns a list of SelectListItem for your Clubs
public List<SelectListItem> GetListOfSelectListItemFromClubsFromSomeWhere()
{
// to do : return List<SelectListItem> with Value and Text(ClubId Id & Name)
}
and in your view, use the Html.DropDownListFor helper method
#model EditSeasonViewModel
#using(Html.Beginform())
{
#Html.DropdownlistFor(x=>x.SelectedClub,
new SelectList(Model.Clubs,"Value","Text"),"select")
<input type="submit" />
}
When this form is being posted, you will get the selected club's id in the SelectedClub property of your posted model.
[HttpPost]
public ACtionResult Create(EditSeasonViewModel model)
{
if(ModelState.IsValid)
{
int clubId= model.SelectedClub;
//to do : save and redirect (PRG pattern)
}
vm.Clubs=GetListOfSelectListItemFromClubsFromSomeWhere();
return View(model);
}
Please be sure that in your edit/creat action controller, the property is correctly binded:
public async Task<ActionResult> Edit([Bind(Include = "SeasonId,ClubId, otherproperties"]){
// code ...
}

saving text box string value to database c#

I have a textbox in which the user can enter their desired username and save it. Once they save it and they happen to revisit their profile page that textbox should be populated with the last username they saved to display and the user will still have the ability to change it and resave. I am fairly new to this and not sure how to start this properly. I am using vs 2012 asp.net mvc 4 c#. Here is my code so far:
#model School.Models.StudentNameModel
#using (Html.BeginForm("_StudentNamePartial", "Profile")) {
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<ol>
<li>
#Html.LabelFor(m => m.StudentName)
#Html.DisplayFor(m => m.StudentName)
#Html.TextBoxFor(m=>m.StudentName)
<button type="button" value="save" />
</li>
</ol>
</fieldset>
}
This is my Model:
public class StudentNameModel
{
[Display(Name = "Student Name")]
public string StudentName{ get; set; }
}
My controller:
GET - To get the student name from database if one exists.
[HttpPost]
public ActionResult _StudentNamePartial(int id)
{
id = WebSecurity.CurrentStudentId;
var model = new StudentNameModel();
using (var db = new StudentsDataContext())
{
var result = (from u in db.Students
where u.ID == id
select u.StudentName).FirstOrDefault();
if(result != null)
model.StudentName= result;
}
return View(model);
}
POST - This is where i want to save the new username for the student
[HttpPost]
public ActionResult _StudentNamePartial(StudentNameModel model)
{
if (ModelState.IsValid)
{
using (var db = new StudentDataContext())
{
try
{
}
catch (Exception)
{
throw;
}
}
return RedirectToAction("ProfileAccount");
}
return View(model);
}
Also i am having trouble that when i am displaying the username it is not hitting my Action method and it always reports that the Object reference is null. Any help will be great. Thanks :D
It would seem that you're trying to render a partial view from a controller action as part of the larger view. In this case, the partial view should be rendered within the ProfileAccount view.
You can structure the controller and views like this (rough outline):
ProfileAccount View Model:
public class ProfileAccountView
{
public StudentNameModel StudentName { get; set; }
}
Profile Controller:
[HttpGet]
public ActionResult ProfileAccount(int id)
{
// Get whatever info you need and store in a ViewModel
var model = new ProfileAccountView();
// Get the student info and store within ProfileAccountView
// Do your database reads
model.StudentName = new StudentNameModel { StudentName = result };
return View(model);
}
[HttpPost]
public ActionResult ProfileAccount(ProfileAccountView profile)
{
// Do whatever processing here
}
ProfileAccount View
#model School.Models.ProfileAccountView
#using (Html.BeginForm("ProfileAccount", "Profile"))
{
#Html.RenderPartial('_StudentNamePartial', Model.StudentName);
<button type="button" value="save" />
}
_StudentNamePartial Partial View
#model School.Models.StudentNameModel
<fieldset>
<ol>
<li>
#Html.LabelFor(m => m.StudentName)
#Html.TextBoxFor(m=>m.StudentName)
</li>
</ol>
</fieldset>

MVC3 submit two model view with dropdownlistfor from database

I'm trying to query List from database which will be shown in dropdownlistfor in view, and chosen result will be post.
The problem is I'm using two models in view I tried more options
I'm lost with this, I don't know how to post it. Data are shown in listbox without problem. I'm getting this error when I try to post:
"There is no ViewData item of type 'IEnumerable' that
has the key 'Item2.idcity'." System.Exception
{System.InvalidOperationException}
CityModel
public class CityModel
{
public int idcity { get; set; }
[Required]
public string cityName { get; set; }
public IEnumerable<CityModel> citys { get; set; }
}
HospitalModel
public class HospitalShowModels
{
[Required]
public string hospitalName { get; set; }
[Required]
public string cityName { get; set; }
}
HospitalControler.Create()
public ActionResult Create()
{
ViewBag.cityModel = new SelectList(DataAccess.DAL.showCity(), "idcity", "cityName");
var tuple = new Tuple<DataAccess.HospitalShowModels, DataAccess.CityModel>(new DataAccess.HospitalShowModels(), new DataAccess.CityModel());
return View(tuple);
}
[HttpPost]
public ActionResult Create(DataAccess.HospitalShowModels model, DataAccess.CityModel model1)
{
if (ModelState.IsValid) {
DataAccess.DAL.insertHospital(model.hospitalName, model1.cityName);
}else{
ModelState.AddModelError("","Invalid options");
}
return View();
}
View.Create
#model Tuple<ProjektZaja.DataAccess.HospitalShowModels,ProjektZaja.DataAccess.CityModel>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Create Hospital</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Item1.hospitalName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Item1.hospitalName)
#Html.ValidationMessageFor(model => model.Item1.hospitalName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Item1.cityName)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => Model.Item2.idcity,(IEnumerable<SelectListItem>)ViewBag.cityModel)
#Html.ValidationMessageFor(model1 => Model.Item2.idcity)
You can't strongly type a view to multiple models. You need to create a view model which will simply be a composite class containing each of the other two classes, and strongly type the view to that view model.
public class CreateViewModel
{
public CityModel CityModel {get; set;}
public HospitalShowModel HospitalShowModel {get; set;}
}
Then strongly type your view to the view model
# model Full.Path.To.CreateViewModel
And access the models properties appropriately
Model.CityModel.cityName //etcetera...
And bind it in the post method
[HttpPost]
public ActionResult Create(CreateViewModel viewModel)
{
//...
The model type in your view should match the model type in the post action. Therefore it should be a Tuple (though I personally wouldn't use that) or a new model type with 2 properties (one for city, one for hospital)

.net mvc4 HttpPost null value

I have a problem while passing an object with HttpPost...
Once the form is submitted, the model is set "null" on the controller side, and I don't know where is the issue..
Here is my controller :
public ActionResult AddUser(int id = 0)
{
Group group = db.Groups.Find(id);
List<User> finalList = db.Users.ToList() ;
return View(new AddUserTemplate()
{
group = group,
users = finalList
});
//Everything is fine here, the object is greatly submitted to the view
}
[HttpPost]
public ActionResult AddUser(AddUserTemplate addusertemplate)
{
//Everytime we get in, "addusertemplate" is NULL
if (ModelState.IsValid)
{
//the model is null
}
return View(addusertemplate);
}
Here is AddUserTemplate.cs :
public class AddUserTemplate
{
public Group group { get; set; }
public User selectedUser { get; set; }
public ICollection<User> users { get; set; }
}
Here is the form which return a null value to the controller (note that the dropdown list is greatly populated with the good values) :
#using (Html.BeginForm()) {
<fieldset>
<legend>Add an user</legend>
#Html.HiddenFor(model => model.group)
#Html.HiddenFor(model => model.users)
<div class="editor-field">
//Here, we select an user from Model.users list
#Html.DropDownListFor(model => model.selectedUser, new SelectList(Model.users))
</div>
<p>
<input type="submit" value="Add" />
</p>
</fieldset>
}
Thanks a lot for your help
I tried your code and in my case the addusertemplate model was not null, but its properties were all null.
That's because of a few model binding issues: Html.HiddenFor and Html.DropDownListFor do not work with complex types (such as Group or User) (at least that's how it is by default).
Also, Html.HiddenFor cannot handle collections.
Here's how to solve these issues:
instead of #Html.HiddenFor(model => model.group) there should be one #Html.HiddenFor for each property of the group that you need bound
instead of #Html.HiddenFor(model => model.users) you need to iterate through the list of users and for each object add #Html.HiddenFor for each property of the user that you need bound
instead of #Html.DropDownListFor(model => model.selectedUser [...], create a property like int SelectedUserId {get;set;} and use that in the DropDownList (as it cannot handle complex types).
Here's the code that works:
1. The User and Group classes, as I imagined them to be:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Group
{
public int Id { get; set; }
public string Name { get; set; }
}
2. The adjusted AddUserTemplate class:
public class AddUserTemplate
{
public Group Group { get; set; }
public IList<User> Users { get; set; }
public int SelectedUserId { get; set; }
public User SelectedUser
{
get { return Users.Single(u => u.Id == SelectedUserId); }
}
}
The adjustments:
Users was changed from ICollection to IList, because we'll need to access elements by their indexes (see the view code)
added SelectedUserId property, that will be used in the DropDownList
the SelectedUser is not a readonly property, that returns the currently selected User.
3. The adjusted code for the view:
#using (Html.BeginForm())
{
<fieldset>
<legend>Add an user</legend>
#*Hidden elements for the group object*#
#Html.HiddenFor(model => model.Group.Id)
#Html.HiddenFor(model => model.Group.Name)
#*Hidden elements for each user object in the users IList*#
#for (var i = 0; i < Model.Users.Count; i++)
{
#Html.HiddenFor(m => m.Users[i].Id)
#Html.HiddenFor(m => m.Users[i].Name)
}
<div class="editor-field">
#*Here, we select an user from Model.users list*#
#Html.DropDownListFor(model => model.SelectedUserId, new SelectList(Model.Users, "Id", "Name"))
</div>
<p>
<input type="submit" value="Add" />
</p>
</fieldset>
}
Another option that does not require a bunch of hidden fields is to simply specify that you want the model passed to the controller. I think this is much cleaner.
#using(Html. BeginForm("action","controller", Model, FormMethod.Post)){
...
}

Categories