I am using Ajax and rendering a Partial View. The data is being passed fine into the controller. There is nothing null. However for some reason in my Partial View the OwnerUser and the AssignToUser are null but the Hours and the Body are coming through with the correct information. I have this same implementation in another part of my project and it's working fine, even with the user coming in. I can't figure out why in this Partial View the ApplicationUser data is null.
[HttpPost]
public ActionResult ChatAjax([Bind(Include = "Body")] Chat chat, string ChatData)
{
chat.Created = DateTimeOffset.Now;
var user = User.Identity.GetUserId();
chat.OwnerUserId = user;
chat.AssignToUserId = ChatData;
db.Chats.Add(chat);
db.SaveChanges();
if (Request.IsAjaxRequest())
{
return PartialView("_Chats", chat);
}
return RedirectToAction("LandingPage", "Admin");
}
This is my Partial View:
#model BugTracker.Models.Chat
#{
var day = DateTimeOffset.Now.Offset.Hours;
}
<div class="direct-chat-msg">
<div class="direct-chat-info clearfix">
<span class="direct-chat-name pull-left">
#Model.OwnerUser.FirstName</span>
<span class="direct-chat-timestamp pull-right">
#Model.Created.AddHours(day)
</span>
<br />
<span class="direct-chat-timestamp pull-right">Sent to
#Model.AssignToUser.DisplayName
</span>
</div>
<!-- /.direct-chat-info -->
<div class="direct-chat-text" style="background:lightblue">
#Model.Body
</div>
<!-- /.direct-chat-text -->
</div>
Here is my Chat Model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace BugTracker.Models
{
public class Chat
{
public int Id { get; set; }
public string Body { get; set; }
public DateTimeOffset Created { get; set; }
[Display(Name = "Owner")]
public string OwnerUserId { get; set; }
[Display(Name = "Assign")]
public string AssignToUserId { get; set; }
public virtual ApplicationUser OwnerUser { get; set; }
public virtual ApplicationUser AssignToUser { get; set; }
}
}
Related
I initially set up my dropdown with submit button which was fine but now I wanted to have it just work without the button (I added onchange). However now I find another difficulty that initially when page is displayed, if I "select" the first option, nothing happens (obviously) so I though to add "please select" option. I found couple of solutions such as writing my custom list of SelectListOptions but this seems like it could be over the top for my case. Could anyone shed some light here and let me know what would be the easiest option here? Sorry if it is simple answer I am really stuck. Here is my code:
Model
public class SurveyDropdownModel
{
public SelectList selectSurveys { get; set; }
public string selectedId { get; set; }
public IEnumerable<RespondentModel> respondents { get; set; }
public SurveyDropdownModel(List<SurveyModel> surveys)
{
selectSurveys = new SelectList(surveys, "SurveyID", "SurveyTitle");
respondents = null;
}
}
public class SurveyModel
{
[Required]
[Display(Name = "Survey ID")]
public int SurveyID { get; set; }
[Display(Name = "Title")]
public string SurveyTitle { get; set; }
[Display(Name = "Updated")]
public DateTime SurveyUpdatedDate { get; set; }
[Display(Name = "Active")]
bool IsActive { get; set; }
}
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
HealthCheckDataLayer.HealthCheckRepository repo = new HealthCheckRepository(connectionString);
List<SurveyModel> surveyList = repo.ReturnSurveys<SurveyModel>();
var model = new SurveyDropdownModel(surveyList);
return View(model);
}
[HttpPost]
public ActionResult Index(SurveyDropdownModel model)
{
//not important here
}
}
View
#model HealthCheckWebApp.Models.SurveyDropdownModel
#{
ViewBag.Title = "Home Page";
}
<div class="row">
<div class="col-md-4">
<h4>Select product:</h4>
#using (Html.BeginForm("Index", "Home"))
{
#Html.DropDownList("selectedId", Model.selectSurveys, new { onchange = "this.form.submit()" })
}
</div>
</div>
<br />
<br />
#if(Model.respondents!=null)
{
#* not relevant here*#
}
I guess now that I didn't include how do I pull my list , I am calling a stored procedure from my repository there (It's required to do it with SP).
Thanks.
Use #Html.DropDownListFor. Here is a description.
Usage:
#Html.DropDownListFor(x=> x.selectedId, Model.selectSurveys, "Select something", new { onchange = "this.form.submit()" )
I have a project (below) where I have Categories and Products. Each category will have multiple products and as you can see from the models, the Category ID is referenced as a Foreign Key. What I am trying to do is to have each Category display as a div with the corresponding products shown in a unordered list beneath. With the code I have, I keep ending up with a div and one product with categories repeating for each product.
I have searched and searched but can't seem to find the solution. Where I am I going wrong?
ViewModel
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace WebApplication1.Web.Models
{
public class Category
{
public int CategoryID { get; set; }
[Required, Display(Name = "Category Name")]
public string CategoryName { get; set; }
public bool IsDisplayedInMainMenu { get; set; }
}
public class Product
{
public int ProductID { get; set; }
public int CategoryID { get; set; }
[ForeignKey("CategoryID")]
[Display(Name = "Category Name")]
public Category CategoryName { get; set; }
[Required, Display(Name = "Product Name")]
public string ProductName { get; set; }
[Required, Display(Name = "Product Description")]
[DataType(DataType.MultilineText)]
public string ProductDescription { get; set; }
}
}
Products Controller
namespace WebApplication1.Controllers
{
public class ProductsController : Controller
{
private ServiceContext db = new ServiceContext();
// GET: Products
public ActionResult Index()
{
var products = db.Products.Include(p => p.CategoryName);
return View(products.ToList());
}
}
}
View
model IEnumerable
#{
ViewBag.Title = "Products";
}
<h2>#ViewBag.Title</h2>
<div class="row">
#foreach (var item in Model)
{
<div class="col-md-3">
<h4><strong>#item.CategoryName.CategoryName</strong></h4>
<ul class="list-unstyled">
#if (item.ProductName.Any())
{
<li>
#item.ProductName
</li>
}
</ul>
</div>
}
</div>
Products
Lumber
2 X 4
Lumber
1 X 4
Lumber
Plywood
Power Tools
Drill
Power Tools
Circular Saw
Garden
Lemon Tree
Garden
Orange Tree
Fasteners
Drywall screws
Fasteners
Deck Screws
Change the data you're sending to the view. Instead of this.DB.Products...try the following:
return this.View(this.DB.Category.Select(x => new { CategoryName = x.CategoryName, Products = x.Products);
...or something similar depending on your database structure. The idea is to have a model that resembles what you actually want to display in the view. This is commonly referred to as a view model.
public class CategoryViewModel
{
public string CategoryName {get; set;}
public IEnumerable<Product> Products {get; set;}
}
If you had such a class, then your controller method might look like this:
return this.View(this.DB.Category.Select(x => new CategoryViewModel { CategoryName = x.CategoryName, Products = x.Products));
Then in your view:
<div class="col-md-3">
<h4><strong>#Model.CategoryName</strong></h4>
#foreach (var item in Model.Products)
{
<ul class="list-unstyled">
#item.ProductName (or something)
</ul>
}
</div>
I'm not on a dev machine so I can't really tell if I've made typos. I'm happy to answer any questions you might have though.
If you are returning products under a selected category, you can use below code
#if(Model.Count()>0)
{
<h4><strong>#Model.ToList()[0].CategoryName.CategoryName</strong></h4>
}
Hope it will works
My problem is that my ViewModel property doesn't bind into an action parameter.
I think it'll be more clear if i just give you my code.
I have a model as follows:
namespace Sima3.Models
{
using System;
using System.Collections.Generic;
public partial class Usuario
{
public string Login { get; set; }
public string NombreCompleto { get; set; }
public short Organigrama { get; set; }
public int Interno { get; set; }
public string EMail { get; set; }
}
}
And i added DataAnnotations to this partial class in another file (Because this model was generated automatically by EntityFramework), note the remote validation on Login property:
namespace Sima3.Models
{
[MetadataType(typeof(UsuarioMetaData))]
public partial class Usuario
{
}
public class UsuarioMetaData
{
[Display(Name = "Nombre de Usuario")]
[Remote("NoExisteUsuario", "Validation")]
[Required]
public string Login { get; set; }
[Display(Name = "Nombre y Apellido")]
[Required]
public string NombreCompleto { get; set; }
[Display(Name = "Sector")]
[Required]
public short Organigrama { get; set; }
[Required]
public int Interno { get; set; }
[EmailAddress]
[Required]
public string EMail { get; set; }
}
}
Now, i have a ViewModel wich contains a property of type Usuario and some other stuff needed to render my View:
namespace Sima3.ViewModels.PedidoViewModels
{
public class AgregarViewModel
{
public Usuario Usuario { get; set; }
public Pedido Pedido { get; set; }
public SelectList ListaSectores { get; set; }
public SelectList ListaEstados { get; set; }
public SelectList ListaPrioridades { get; set; }
public SelectList ListaTipos { get; set; }
public SelectList ListaDirecciones { get; set; }
}
}
And my view looks as follows (I'll only post part of it, if u need to see more let me know):
#using (Ajax.BeginForm("Crear", "Usuario", null,
new AjaxOptions
{
OnSuccess = "UsuarioCreadoSuccess",
HttpMethod = "Post"
}
, new { #class = "form-horizontal", id = "FormUsuario" }))
{
#Html.AntiForgeryToken()
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title title">Nuevo Usuario</h4>
</div>
<div class="modal-body">
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Usuario.Login, new { #class = "col-lg-4 control-label" })
<div class="col-lg-7">
#Html.TextBoxFor(model => model.Usuario.Login, new { #class = "form-control"})
#Html.ValidationMessageFor(model => model.Usuario.Login)
</div>
</div>
Alright, the important part is the TextBoxFor, it renders me the next HTML:
<input class="form-control valid" data-val="true" data-val-remote="'Nombre de Usuario' is invalid." data-val-remote-additionalfields="*.Login" data-val-remote-url="/Validation/NoExisteUsuario" data-val-required="El campo Nombre de Usuario es obligatorio." id="Usuario_Login" name="Usuario.Login" type="text" value="">
As you see, the textbox name is: name="Usuario.Login"
And my controller action wich gets called by the Remote validation looks like this:
public JsonResult NoExisteUsuario([Bind(Prefix="Usuario")]string Login)
{
Usuario usuario = db.Usuarios.Find(Login);
if (usuario != null)
{
var MensajeDeError = string.Format("El usuario {0} ya existe", Login);
return Json(MensajeDeError, JsonRequestBehavior.AllowGet);
}
else
{
return Json(true, JsonRequestBehavior.AllowGet);
}
}
I set a breakpoint in this Action and it gets hit, but Login comes in null.
I checked with google chrome's debugger the http request header and it shows the form is getting submitted like this: Usuario.Login: asdasdasd.
The question is simple, how can i make it bind?
By the way, i'm using MVC5.
Thanks.
Well, i finally got it working, it seems i was setting the Prefix binding attribute wrongly.
Now my action looks like this:
public JsonResult NoExisteUsuario([Bind(Prefix="Usuario.Login")]string login)
{
Usuario usuario = db.Usuarios.Find(login);
if (usuario != null)
{
var MensajeDeError = string.Format("El usuario {0} ya existe", login);
return Json(MensajeDeError, JsonRequestBehavior.AllowGet);
}
else
{
return Json(true, JsonRequestBehavior.AllowGet);
}
}
I create a website for my wife. She's a teacher and she would like to have a possibility to create exercises for their students. The case is that she would like to create for instance the following exercise:
Exercise 1: Fill the sentence using a correct word:
My wife is 30 ............. old
I live in this city for 30 .........
I have the following model:
public class Exercise
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ExerciseType Type { get; set; }
public DifficulityLevel DifficulityLevel { get; set; }
public List<ExerciseItem> Items { get; set; }
public DateTime TimeOfCreation { get; set; }
public DateTime TimeOfModification { get; set; }
}
public class ExerciseItem
{
[Key]
public Guid Id { get; set; }
public string Content { get; set; }
public List<ExerciseItemOption> Options { get; set; }
public ExerciseItemOption CorrectSelection { get; set; }
}
I creates a View for my Exercise. I can fill in the basic properties like Name, Description, Difficulity Level and Type. Then I would like to create a button "Add exercise item". When clicked, a partial view (or something else) should be added dynamically where new ExerciseItem can be provided.
I've tried to following:
I've added a button
#Ajax.ActionLink("Add exercise item",
"AddExerciseItem",
"Exercise", new AjaxOptions() { HttpMethod="GET", InsertionMode = InsertionMode.InsertBefore, UpdateTargetId="ExerciseItems"})
and the appropriate div:
<div id="ExerciseItems"></div>
My action method looks as follows:
public ActionResult AddExerciseItem()
{
return PartialView("ExerciseItem", new ExerciseItem());
}
and the partial view:
#model ElangWeb.Models.ExerciseItem
<fieldset>
<legend>ExerciseItem</legend>
#Html.HiddenFor(model => model.Id)
<div class="editor-label">
#Html.DisplayNameFor(model => model.Content)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Content, new { style = "width:200px" })
</div>
</fieldset>
It works fine. However when I click button for creating a whole exercise, I do not have ExerciseItem collection in my model:
public ActionResult Create(Exercise exercise)
{
using (PersistanceManager pm = new PersistanceManager())
{
exercise.Id = Guid.NewGuid();
exercise.TimeOfCreation = DateTime.Now;
exercise.TimeOfModification = DateTime.Now;
pm.ExcerciseRepository.Add(exercise);
}
return RedirectToAction("Index");
}
How should I change the code in order to bind my list of added ExerciseItem objects to my model Exercise?
Check out this article about model binding. You basically need to create special names for the exercise items so that they get bound correctly.
e.g. partial:
#model ElangWeb.Models.ExerciseItem
<fieldset>
<legend>ExerciseItem</legend>
<label>content</label>
<input type="hidden" name="ExcersiseItem.Index" value="SomeUniqueValueForThisItem" />
<input type="text" name="ExcersiseItem[SomeUniqueValueForThisItem].Name" value="#Model.Content" />
</fieldset>
You can also look at my answer to this question MVC3 Non-Sequential Indices and DefaultModelBinder. Thanks Yarx for finding it, I was actually trying to find it :)
I have a view that is created dynamically with objects from my database. How can I take the values from my View and pass them to my view model if it is created dynamically? I know this question is vague but hopefully looking through my code will help you help me.
View
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm()) {%>
<h1>Survey Says...</h1>
<ol>
<li>
Name: <input type="text" disabled="true" name="name" value="<%= Model.Name %>"/>
</li>
<li>
Email: <input type="text" disabled="true" name="email" value="<%= Model.Email %>"/>
</li>
<%foreach(SurveyQuestions s in Model.myQuestions)
{ %>
<li> <%= s.QuestionText %> <br />
<%
foreach(SurveyQuestionAnswers q in s.QuestionAnswers)
{
%>
<input type="radio" name="rbGroup<%= q.Id%>"/> <%= q.DisplayText %><br/>
<%
if(q.IsEditable)
{
%>
<input type="text" id="txtOther<%= q.Id %>"/>
<%
}
}
%>
</li>
<% }
%>
<li>
<input type="submit" value="Save" id="save-button" />
</li>
</ol>
<% } %>
SurveyQuestion class
public class SurveyQuestions
{
public string QuestionText { get; set; }
public List<SurveyQuestionAnswers> QuestionAnswers { get; set; }
public int Id { get; set; }
}
SurveyQuestionAnswers class
public class SurveyQuestionAnswers
{
public int Id { get; set; }
public string DisplayText { get; set; }
public bool IsEditable { get; set; }
}
My sloppy ViewModel
public class GfcPreInterventionSurveyViewModel
{
static SurveyService myService = new SurveyService(new SurveyRepository());
[DisplayName("Name")]
[Required(ErrorMessage = "Name is required.")]
public string Name { get; set; }
[DisplayName("Work Email")]
[Email(ErrorMessage = "The email you entered is not valid.")]
public string Email { get; set; }
[DisplayName("Gender")]
[Required(ErrorMessage = "Gender is required.")]
public string Gender { get; set; }
[DisplayName("Country")]
[Required(ErrorMessage = "Country is required.")]
public string Country { get; set; }
[DisplayName("Business")]
[Required(ErrorMessage = "Please select a business unit.")]
public string BusinessUnit { get; set; }
public SelectList GenderList;
public SelectList BusinessList;
public SelectList CountryList;
public SelectList RoutineList;
public SelectList ReasonList;
public SelectList ActivityList;
public SelectList HealthList;
public SelectList EnergyList;
public SelectListItem GenderItem;
public SelectListItem BusinessItem;
public SelectListItem CountryItem;
public SelectListItem RoutineItem;
public SelectListItem ReasonItem;
public SelectListItem ActivityItem;
public SelectListItem HealthItem;
public SelectListItem EnergyItem;
public Boolean ToGetFit { get; set; }
public Boolean ToChallengeMyself { get; set; }
public Boolean ToIncrEnergy { get; set; }
public Boolean ToBuildMorale { get; set; }
public Boolean ToBeHealthier { get; set; }
public Boolean ChallengeOther { get; set; }
public string OtherString { get; set; }
[DisplayName("Exercise Routine")]
[Required(ErrorMessage = "Which option describes your exercise routine.")]
public string Routine { get; set; }
[DisplayName("Physical Activity")]
[Required(ErrorMessage = "Which option describes your physical activity.")]
public string Activity { get; set; }
[DisplayName("Overall Health")]
[Required(ErrorMessage = "Which option describes your overall health.")]
public string Health { get; set; }
[DisplayName("Energy")]
[Required(ErrorMessage = "Which option best describes your energy.")]
public string Energy{ get; set; }
public int ReasonsForChallenge { get; set; }
public List<SurveyQuestions> myQuestions = new List<SurveyQuestions>();
//public List<SurveyQuestionAnswers> myAnswers;
public void build(int id)
{
var myService = new SurveyService(new SurveyRepository());
myQuestions = myService.GetSurveyQuestions(id);
}
my getSurveyQuestions method returns a List of SurveyQuestions objects.
currently, in my controller, when save is hit, the post method is called. this is where i want to update my database with the values in the viewmodel, but because my page is so dynamic, i am having trouble accessing the user's input.
My Controller:
public class SurveyController : WidgetControllerBase
{
#region Private Members
private readonly ISurveyRepository _surveyRepository;
#endregion
#region Constructors
public SurveyController(ISurveyRepository surveyRepository)
{
if (surveyRepository == null)
{
throw new ArgumentNullException("surveyRepository");
}
_surveyRepository = surveyRepository;
}
#endregion
// GET: /Survey/GfcPreIntervention
public ActionResult GfcPreInterventionSurvey()
{
var surveyService = new SurveyService();
var vm = new GfcPreInterventionSurveyViewModel();
vm.build(AppUserInstance.Id);
//paymentService.SetDVDShipped(payment.PaymentId);
return View(vm);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult GfcPreInterventionSurvey(GfcPreInterventionSurveyViewModel viewModel)
{
return View(viewModel);
}
}
Since your page is highly dynamic, maybe you would be better off writing a custom model binder. Though the term sounds intimidating it’s not actually that complicated.
You would simply create a class which would implement IModelBinder. Within this class, you would have access to the form collection and can populate any complex object(s) with it
Following are some examples worth looking at
http://dotnetslackers.com/articles/aspnet/Understanding-ASP-NET-MVC-Model-Binding.aspx
http://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/
And as you have figured out, using helper methods (preferably with Razor) will clean up the UI code