The model binding worked fine until i implemented interfaces on top of the following classes:
public class QuestionAnswer : IQuestionAnswer
{
public Int32 Row_ID { get; set; }
public Int32 Column_ID { get; set; }
public String Value { get; set; }
}
public class HiddenAnswer : IHiddenAnswer
{
public Int32 Hidden_Field_ID { get; set; }
public String Hidden_Field_Value { get; set; }
}
public class SurveyAnswer : ISurveyAnswer
{
public string SessionID { get; set; }
public List<IQuestionAnswer> QuestionAnswerList { get; set; }
public List<IHiddenAnswer> HiddenAnswerList { get; set; }
public SurveyAnswer()
{
QuestionAnswerList = new List<IQuestionAnswer>();
HiddenAnswerList = new List<IHiddenAnswer>();
}
}
Now that the interfaces are there, i get a 500 (Internal Server Error)
The javascript that i use to model bind is the following:
$('#submitbutton').click(function () {
var answers = new Array();
var hiddenfields = new Array();
var formname = "#" + $("#formname").val();
$(':input', formname).each(function () {
if ($(this).is(":text") || $(this).is(":radio") || $(this).is(":checkbox"))
{
var answerObject = {
Column_ID: $(this).attr('data-column_id'),
Row_ID: $(this).attr('data-row_id'),
Value: $(this).attr('data-theValue')
};
answers.push(answerObject);
}
else if($(this).is(":hidden")) {
var hiddenObject =
{
Hidden_Field_ID: $(this).attr('data-hidden_field_id'),
Hidden_Field_Value: $(this).attr('data-hidden_field_value')
}
hiddenfields.push(hiddenObject);
}
});
$('textarea', formname).each(function () {
var answerObject = {
Column_ID: $(this).attr('data-column_id'),
Row_ID: $(this).attr('data-row_id'),
Value: $(this).val(),
};
answers.push(answerObject);
});
var allAnswers = {
SessionID: 0,
QuestionAnswerList: answers,
HiddenAnswerList: hiddenfields
}
postForm(allAnswers);
});
The Controller Action looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SubmitSurvey(SurveyAnswer answers)
{
// Dette tillader CORS
Response.AppendHeader("Access-Control-Allow-Origin", "*");
bc.SaveSurvey(answers);
return null;
}
what am i doing wrong?
what am i doing wrong?
You cannot expect the model binder to know that when it encounters the IQuestionAnswer interface on your SurveyAnswer view model it should use the QuestionAnswer type. It's nice that you have declared this implementation of the interface but the model binder has no clue about it.
So you will have to write a custom model binder for the IQuestionAnswer interface (same for the IHiddenAnswer interface) and indicate which implementation do you want to be used:
public class QuestionAnswerModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var type = typeof(QuestionAnswer);
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
which will be registered in your Application_Start:
ModelBinders.Binders.Add(typeof(IQuestionAnswer), new QuestionAnswerModelBinder());
Related
Json response:
{
"ID": 1,
"Value": 10,
"User": null
}
I need to change the User value in each response in OnActionExecuted method.
public override void OnActionExecuted(ActionExecutedContext context)
{
var response = context.Result;
}
But unable to read the Result and Update details.
You just need to do some casting, I think. This code needs some error handling but otherwise demonstrates what you want:
public class ViewModel
{
public int ID { get; set; }
public int Value { get; set; }
public string User { get; set; }
}
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IActionResult Get()
{
var vm = new ViewModel()
{
ID = 1,
Value = 10
};
return Ok(vm);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
var result = context.Result as OkObjectResult;
var vm = result.Value as ViewModel;
vm.User = "ardalis";
}
}
The result:
{"id":1,"value":10,"user":"ardalis"}
In mvc4 i am trying to Post model data from view to controller using Jquery Ajax but don't now what's wrong with this code can any one help me in this matter.
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/jquery-1.8.2.min.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#save").click(function () {
$("#content").html("<b>Please Wait...</b>");
var dataObject = {
empid: 1,
EmployeeName: "rizwan",
Address: "lahore",
Country: "pakistan",
Salary: "35000.00",
DepartmentName: "Field"
}
$.ajax({
type: "POST",
url: "/Home/Index",
data: dataObject,
success: function (data)
{
$("#empname").val(''),
$("#empadd").val(''),
$("#empcountry").val(''),
$("#empsalary").val(''),
$("#empdeptname").val(''),
$("#content").html("<div class='success'>"+data+"</div>")
},
error: function (ehr)
{
$("#content").html("<div class='failed'>Error! Please try again</div>");
},
})
});
});
</script>
This is my controller action code who just receive the value of object and save into database
Problem is that i failed to receive values at controller action side.
Please help me.....
[HttpPost]
public ActionResult Index(userview dataObject)
{
department dept = new department();
employee emp = new employee();
string message = "";
try
{
emp.employeeName = dataObject.EmployeeName;
emp.address = dataObject.Address;
emp.country = dataObject.Country;
emp.salary = dataObject.Salary;
dept.departmentName = dataObject.DepartmentName;
db.employees.Add(emp);
db.departments.Add(dept);
db.SaveChanges();
}
catch(Exception ex)
{
message = "Error! Please try again";
}
if (Request.IsAjaxRequest())
{
return new JsonResult { Data = message, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
ViewBag.message = message;
return View();
}
This is my model class
public class userview
{
public int empId { get; set; }
public string EmployeeName { get; set; }
public string Address { get; set; }
public string Country { get; set; }
public decimal Salary { get; set; }
public string DepartmentName { get; set; }
}
Try using JSON.stringify
$.ajax({
type: "POST",
url: "/Home/Index",
data: JSON.stringify(dataObject), //Here is the change
success: function (data)
{
$("#empname").val(''),
$("#empadd").val(''),
$("#empcountry").val(''),
$("#empsalary").val(''),
$("#empdeptname").val(''),
$("#content").html("<div class='success'>"+data+"</div>")
},
error: function (ehr)
{
$("#content").html("<div class='failed'>Error! Please try again</div>");
},
})
You can implement BindModel yourself! get the json string and deserialize to your entity.
public class JsonBinder<T> : System.Web.Mvc.IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
using (var reader = new System.IO.StreamReader(controllerContext.HttpContext.Request.InputStream))
{
//set stream position 0, maybe previous action already read the stream.
controllerContext.HttpContext.Request.InputStream.Position = 0;
string json = reader.ReadToEnd();
if (string.IsNullOrEmpty(json) == false)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
object jsonData = serializer.DeserializeObject(json);
return serializer.Deserialize<T>(json);
}
else
{
return null;
}
}
}
}
and set the JsonBinder to the post method like
[HttpPost]
public ActionResult Index([ModelBinder(typeof(JsonBinder<userview>))] userview dataObject)
{
}
the other solution
I found that you could set DataContract to the class of Model, and set DataMember to the Properties of the class.
edit the class like this
[DataContract]
public class userview
{
[DataMember]
public int empId { get; set; }
[DataMember]
public string EmployeeName { get; set; }
[DataMember]
public string Address { get; set; }
[DataMember]
public string Country { get; set; }
[DataMember]
public decimal Salary { get; set; }
[DataMember]
public string DepartmentName { get; set; }
}
and you should add library reference "System.Runtime.Serialization"
Hope it works for you.
I'm trying to create my profile type page for my simple blog site. I have two simple model class like this:
public class UserInfoModel
{
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmPassword { get; set; }
}
public class NewPost
{
public string PostTitle { get; set; }
public string PostStory { get; set; }
}
I have created a joint model class of user & post to pass to view like this:
public class UserPostModel
{
public UserInfoModel User { get; set; }
public NewPost Post { get; set; }
}
The methods I wrote to retrieve the user & post info are like this:
public int GetUserID(string _UserName)
{
using (var context = new TourBlogEntities1())
{
var UserID = from s in context.UserInfoes
where s.UserName == _UserName
select s.UserID;
return UserID.Single();
}
}
public UserInfo GetUserDetails(int _UserID)
{
using (var context = new TourBlogEntities1())
{
var UserDetails = (from s in context.UserInfoes
where s.UserID == _UserID
select s).Single();
return UserDetails;
}
}
public Post GetUserPosts(int _UserID)
{
using (var context = new TourBlogEntities1())
{
var entity = (from s in context.Posts
where s.UserID == _UserID
select s).Single();
return entity;
}
}
And finally I'm calling all my method from my controller action like this:
[Authorize]
public ActionResult MyProfile()
{
var Business = new Business();
var UserID=Business.GetUserID(User.Identity.Name);
var UserEntity=Business.GetUserDetails(UserID);
var PostEntity=Business.GetUserPosts(UserID);
var model = new UserPostModel();
model.User.UserName = UserEntity.UserName; // problem showing here
model.User.Email = UserEntity.Email;
model.Post.PostTitle = PostEntity.PostTitle;
model.Post.PostStory = PostEntity.PostStory;
return View("MyProfile",model);
}
A run time error showing like " object is not referenced to a object type or null object". I worked ok in a very similar way while passing single model. Whats I'm doing wrong here?
Modified your UserPostModel
public class UserPostModel
{
public UserPostModel()
{
User = new UserInfoModel();
Post = new Post();
}
public UserInfoModel User { get; set; }
public NewPost Post { get; set; }
}
NOTE: check each value before set to model it should not be null.
I'm using knockoutjs to render a collection of items. After allowing the user to do some inline editing I need to post the collection back to the server. However, the collection isn't being populated on the server because I'm not using the name="[0].Blah" naming convention. Does anyone know how to either render name attributes like this using knockoutjs OR how to create a model binder that will allow me to extract the values from the ValueProvider?
You can see a screenshot of the ValueProvider during debugging below.
http://i.imgur.com/zSU5Z.png
Here is my managed ViewModel:
public class FundLevelInvestmentUploadResult
{
public string FileName { get; set; }
public IList<FundLevelInvestmentViewModel> Items { get; set; }
public int NumberOfErrors { get; set; }
public bool ShowErrorsOnly { get; set; }
public FundLevelInvestmentUploadResult()
{
Items = new List<FundLevelInvestmentViewModel>();
}
}
Here is the managed class for "Items":
public class FundLevelInvestmentViewModel
{
private string _fund;
private string _fundType;
private string _date;
private string _netOfWaivedFees;
private string _waivedFees;
private string _bcip;
private string _fxRate;
public uint RowIndex { get; set; }
public int? DealCode { get; set; }
public bool DealCodeIsValid { get; set; }
public string Fund
{
get { return _fund; }
set { _fund = GetString(value); }
}
public bool FundIsValid { get; set; }
public string FundType
{
get { return _fundType; }
set { _fundType = GetString(value); }
}
public bool FundTypeIsValid { get; set; }
public string DateOfInvestment
{
get { return _date; }
set { _date = GetString(value); }
}
public bool DateOfInvestmentIsValid { get; set; }
public string NetOfWaivedFees
{
get { return _netOfWaivedFees; }
set { _netOfWaivedFees = GetString(value); }
}
public bool NetOfWaivedFeesIsValid { get; set; }
public string WaivedFee
{
get { return _waivedFees; }
set { _waivedFees = GetString(value); }
}
public bool WaivedFeeIsValid { get; set; }
public string BCIP
{
get { return _bcip; }
set { _bcip = GetString(value); }
}
public bool BCIPIsValid { get; set; }
public string ExchangeRateToUSD
{
get { return _fxRate; }
set { _fxRate = GetString(value); }
}
public bool ExchangeRateToUSDIsValid { get; set; }
public string FileName { get; set; }
private IList<string> _errors;
public IList<string> Errors
{
get { return _errors ?? (_errors = new List<string>());}
set { _errors = value; }
}
public bool Show { get; set; }
public FundLevelInvestmentViewModel()
{
Errors = new List<string>();
Show = true;
}
// knockoutjs is returning "null" instead of "" for a null object when calling ko.mapping.fromJS
private string GetString(string value)
{
if (value == "null")
return string.Empty;
return value;
}
}
Here is my knockout viewModel:
var viewModel = {
FileData: ko.observableArray([]),
validateFile: function (file, event) {
$.ajax({
type: 'post',
url: newUrl,
data: ko.mapping.toJS(file)
}).done(function (data) {
var newFile = ko.mapping.fromJS(data);
var index = file.Items.indexOf(file);
viewModel.FileData.replace(file, newFile);
});
}
};
If you are using version 2.1.0.0 or later of knockout you can render the name attribute as follows from an observable array.
<input data-bind='attr: { name: "Items["+$index()+"].DealCode"}' />
I have a model like so:
return new MyViewModel()
{
Name = "My View Model",
Modules = new IRequireConfig[]
{
new FundraisingModule()
{
Name = "Fundraising Module",
GeneralMessage = "Thanks for fundraising"
},
new DonationModule()
{
Name = "Donation Module",
MinDonationAmount = 50
}
}
};
The IRequireConfig interface exposes a DataEditor string property that the view uses to pass to #Html.EditorFor like so:
#foreach (var module in Model.Modules)
{
<div>
#Html.EditorFor(i => module, #module.DataEditor, #module.DataEditor) //the second #module.DataEditor is used to prefix the editor fields
</div>
}
When I post this back to my controller TryUpdateModel leaves the Modules property null. Which is pretty much expected since I wouldnt expect it to know which concrete class to deserialize to.
Since I have the original model still available when the post comes in I can loop over the Modules and get their Type using .GetType(). It seems like at this point I have enough information to have TryUpdateModel try to deserialize the model, but the problem is that it uses a generic type inference to drive the deserializer so it does not actually update any of the properties except the ones defined in the interface.
How can I get update my Modules array with their new values?
If any particular point isnt clear please let me know and I will try to clarify
You could use a custom model binder. Assuming you have the following models:
public interface IRequireConfig
{
string Name { get; set; }
}
public class FundraisingModule : IRequireConfig
{
public string Name { get; set; }
public string GeneralMessage { get; set; }
}
public class DonationModule : IRequireConfig
{
public string Name { get; set; }
public decimal MinDonationAmount { get; set; }
}
public class MyViewModel
{
public string Name { get; set; }
public IRequireConfig[] Modules { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
Name = "My View Model",
Modules = new IRequireConfig[]
{
new FundraisingModule()
{
Name = "Fundraising Module",
GeneralMessage = "Thanks for fundraising"
},
new DonationModule()
{
Name = "Donation Module",
MinDonationAmount = 50
}
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
View:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.Name)
for (int i = 0; i < Model.Modules.Length; i++)
{
#Html.Hidden("Modules[" + i + "].Type", Model.Modules[i].GetType())
#Html.EditorFor(x => x.Modules[i])
}
<input type="submit" value="OK" />
}
and finally the custom model binder:
public class RequireConfigModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeParam = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Type");
if (typeParam == null)
{
throw new Exception("Concrete type not specified");
}
var concreteType = Type.GetType(typeParam.AttemptedValue, true);
var concreteInstance = Activator.CreateInstance(concreteType);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => concreteInstance, concreteType);
return concreteInstance;
}
}
which you would register in Application_Start:
ModelBinders.Binders.Add(typeof(IRequireConfig), new RequireConfigModelBinder());
Now when the form is submitted the Type will be sent and the model binder will be able to instantiate the proper implementation.