I'm having problem with sending json from knockout to mvc2 controller action.
Here is what I have in my View:
var ViewModel = {
FirstName: ko.observable("FirstName"),
LastName: ko.observable("LastName"),
Save: function () {
ko.utils.postJson(location.href, this);
}
}
ko.applyBindings(ViewModel);
I have an action in controller:
public virtual ActionResult SomeAction(MyModel model) {
//do smth
return View(registrationModel);
}
public class MyModel {
public string FirstName {get;set;}
public string LastName {get;set;}
}
The problem is that i'm getting string values quoted, like "\"FirstName\"", and I know that there is some way to avoid that(work with JSON.stringify in MVC3).
I've tried the following:
ko.utils.postJson(location.href, JSON.stringify({model: this});
also
var json = JSON.stringify({
FirstName: this.FirstName(),
LastName: this.LastName()
});
ko.utils.postJson(location.href, JSON.stringify({model: json});
or
ko.utils.postJson(location.href, json);
In all these 3 options I get model = null, or all the values null in Controller.
Maybe someone has done this before?
I've found that in order for the MVC object mapping to work you need to set the content type of the POST to be "application/json; charset=utf-8". I've never done this using ko.utils.postJson() before but here's a working example using jQuery:
$.ajax({
url: url,
type: "POST",
data: ko.toJSON(ViewModel),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (response) {
},
error: function (response, errorText) {
}
});
Note I'm using ko.toJSON to serialize the model as JSON.
Related
I have successfully created one route in ember-cli-mirage, but am having trouble loading the related data.
The API should be returning JSON API compliant data.
I'm not really sure if there are any good methods or not for debugging mirage's request interception. Here is my config.js
export default function() {
this.urlPrefix = 'https://myserver/';
this.namespace = 'api/v1';
this.get('/machines', function(db, request) {
return {
data: db.machines.map(attrs => (
{
type: 'machines',
id: attrs.id,
attributes: attrs
}
))
};
});
this.get('/machines/:id', function(db, request){
let id = request.params.id;
debugger;
return {
data: {
type: 'machines',
id: id,
attributes: db.machines.find(id),
relationships:{
"service-orders": db["service-orders"].where({machineId: id})
}
}
};
});
this.get('/machines/:machine_id/service-orders', function(db, request){
debugger; // this never gets caught
});
}
Most of this is working fine (I think). I can create machines and service orders in the factory and see the db object being updated. However, where my application would normally make a call to the api for service-orders: //myserver/machines/:machine_id/service-orders, the request is not caught and nothing goes out to the API
EDIT:
This is the route that my Ember app is using for /machines/:machine_id/service-orders:
export default Ember.Route.extend(MachineFunctionalRouteMixin, {
model: function() {
var machine = this.modelFor('machines.show');
var serviceOrders = machine.get('serviceOrders');
return serviceOrders;
},
setupController: function(controller, model) {
this._super(controller, model);
}
});
And the model for machines/show:
export default Ember.Route.extend({
model: function(params) {
var machine = this.store.find('machine', params.machine_id);
return machine;
},
setupController: function(controller, model) {
this._super(controller, model);
var machinesController = this.controllerFor('machines');
machinesController.set('attrs.currentMachine', model);
}
});
Intuitively, I would think that machine.get('serviceOrders'); would make a call to the API that would be intercepted and handled by Mirage. Which does not seem to be the case
I have a basic form in ASP.Net MVC4 using Html helpers. I have an input file in the form for uploading a file which will be added to a database. In my view model I have a property for the input file:
public HttpPostedFileBase AssetFile { get; set; }
In my view I have the form helper:
#using (Html.BeginForm("Create", "Contact", FormMethod.Post, new { enctype = "multipart/form-data" }))
Inside my form:
#Html.TextBoxFor(model => model.AssetFile, new { #type = "file", #name = "AssetFile" })
Yet, when I post the form there are no files in the Request.Files. Then I noticed that in Fiddler the Request Header has Content-Type: application/x-www-form-urlencoded; charset=UTF-8. I have another form with an input file and the header has Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryCfBFSR0RXQRnasfb and this form works fine. I tried doing this without Html helpers and same thing happens. The view model itself inherits from another view model where the input file actually belongs but then I have a string property mapped to a textbox and the form is picking up this value. There is no nested forms (on this page this is the only form) but I am having the same problem with another page that has multiple form (not nested) using multiple partial views that contain the forms like this:
#using (Html.BeginForm("Edit", "Home", FormMethod.Post, new { #enctype = "multipart/form-data" })){ #Html.Partial("EditorTemplates/_Profile", Model.InstitutionProfileInformation)}
Thanks in advance for any help.
OK - Here's the weirdness. Since they're (the original coder(s)) using partial views they ajaxified this thing. When the partial view (with the form) is rendered:
var loadContactDiv = function (e) {
isChanged = false;
var url = e.href;
$("#Contacts").load(url, function (response, status, xhr) {
if (status == "error") {
var msg = "Sorry but there was an error: ";
}
$("#Contacts").find('form').submit(divSubmitHandler).change(function () {
isChanged = true;
});
$("#ReturnButton").click(function () {
loadContacts();
});
});
return false;
};
Then when the user click the submit button:
var divSubmitHandler = function () {
var form = $("#Contacts").find('form');
var test = form.serialize();
debugger;
$.ajax({
url: (form).attr('action'),
data: form.serialize(),
type: "POST",
success: function (data) {
if (data == "") {
loadContacts();
} else {
$("#Contacts").html(data);
$("#Contacts").find('form').submit(divSubmitHandler).change(function () {
isChanged = true;
});
$("#ReturnButton").click(function (e) {
loadContacts();
});
}
}
});
return false;
};
Still stuck: http://prntscr.com/20v2cp
Since you are not submitting the form, but using $.ajax to process the request remotely and then get the result, the enctype is ignored from the form itself.
As you can also see the form data is serialize and sent.
So the fix here is simple, to submit the content-type correctly, just add a
content-type
option to the ajax request like so,
var divSubmitHandler = function () {
var form = $("#Contacts").find('form');
var test = form.serialize();
debugger;
$.ajax({
url: (form).attr('action'),
**contentType: 'multipart/form-data',**
data: form.serialize(),
type: "POST",
success: function (data) {
if (data == "") {
loadContacts();
} else {
$("#Contacts").html(data);
$("#Contacts").find('form').submit(divSubmitHandler).change(function () {
isChanged = true;
});
$("#ReturnButton").click(function (e) {
loadContacts();
});
}
}
});
return false;
};
This should do the trick. However if it does not work, please refer to Jquery Form Ajax submit.
jQuery AJAX submit form
Have a nice session!
I use WebApi in MVC 4 with EF, Ninject.
In client i use knockout.js end it's delete my entries from UI, but it's still in DB.
Add, update action works fine.
Client code:
self.removeUser = function (user) {
var conj = ko.toJS(user);
var json = JSON.stringify(conj);
var Id = user.Id();
$.ajax({
url: API_URL + Id,
cache: false,
type: 'DELETE',
contentType: 'application/json; charset=utf-8',
data: '',
success: function () {
self.Users.remove(user);
}
});
}
WebAPI
// DELETE api/user/5
public HttpResponseMessage Delete(int id)
{
_userRepository.Delete(id);
return Request.CreateResponse(HttpStatusCode.NoContent);
}
Repository
public void Delete(int userId)
{
var user = Get(userId);
_db.Users.Remove(user);
}
Where to start? Which way to go?
If you want to delete an entity is is not enough to remove it form its collection. You need to call DbContext.SaveChanges() in order to commit the transaction and persist your changes into the DB:
public void Delete(int userId)
{
var user = Get(userId);
_db.Users.Remove(user);
_db.SaveChanges();
}
Actually in one request, i am getting the data from the ajax call and then posting the data back in another request back to the controller and here i am using model binding,
here i have the following issues,
1. The dictionary in the bound object is null
2. The Id property [value received at Ajax response] but not received at controller during modelbinding
since i am not storing any values in the hidden fields and am just caching the data and passing to other requests, how do i handle the id and dictionary issues in ASP.Net MVC2.
EDIT
*Model in C#:*
public class ViewObject
{
public string Id {get;set;}
public string Name {get;set;}
}
AJAX Code
function fillExistingViews() {
$.ajax({
url: "..",
data: {
ViewId: $("#View_Id").val()
},
type: "POST",
success: function (data) {
if (data !== undefined) {
var ViewObj = JSON.parse(data);
if (ViewObj.ViewObjects === undefined) {
return false;
}
//ViewObj.ViewObjects = dictionary<string,ViewObject
for (var vo in ViewObj.ViewObjects) {
// HERE I GET ViewObj.ViewObjects[vo].Id
$.ajax({
url: "..",
type: "POST",
contentType: "application/json",
data: JSON.stringify(ViewObj.ViewObjects[vo]),
// on posting the viewobject to the server, i find ViewObject.Id to be null
success: function (data) {
//..
}
});
}
}
}
});
}
I've looked around on SO to see if this has been asked before and couldn't find anything (so if it has indeed been asked before then I apologize.
Here's what I'm trying to do, a user can select from a list of skills for their profile, if a skill they want isn't in the list then they can add it to the database, I have that accomplished with WCF & jQuery AJAX. Here's the code for that:
$("#AddNewSkill").click(function () {
$("#AddNewSkill").attr("disabled", true);
$("#newSkill").attr("disabled", true);
var newSkill = $("#newSkill").val();
var data = { name: $("#newSkill").val(), description: "", type: "Skill" };
data = JSON.stringify(data)
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "../WeddingPhotographerService.svc/AddNew",
data: data,
dataType: "json",
success: function () {
successCall('#newSkill', '#AddNewSkill');
//$('#SkillListViewContainer').load('../Account/GetSkillControl');
},
error: function (msg) {
$("#AddSkillError").text(msg.d);
$("#AddSkill").attr("disabled", false);
$("#NewSkill").attr("disabled", false);
}
});
});
Here's the method in the AJAX-Enabled WCF service:
[OperationContract]
public bool AddNew(string name, string description, string type)
{
switch (type)
{
case "":
goto default;
case "skill":
IRepository<Skill> skillRepo = ObjectFactory.GetInstance<IRepository<Skill>>();
var skill = new Skill { Name = name, Description = (string.IsNullOrEmpty(description)) ? string.Empty : description };
skillRepo.Save(skill);
return true;
case "equipment":
IRepository<Equipment> eqRep = ObjectFactory.GetInstance<IRepository<Equipment>>();
var eq = new Equipment { Name = name, Description = (string.IsNullOrEmpty(description)) ? string.Empty : description };
eqRep.Save(eq);
return true;
case "occasion":
IRepository<Occassion> occRepo = ObjectFactory.GetInstance<IRepository<Occassion>>();
var oc = new Occassion { Name = name, Description = (string.IsNullOrEmpty(description)) ? string.Empty : description };
occRepo.Save(oc);
return true;
default:
IRepository<Skill> repo = ObjectFactory.GetInstance<IRepository<Skill>>();
var s = new Skill { Name = name, Description = (string.IsNullOrEmpty(description)) ? string.Empty : description };
repo.Save(s);
return true;
}
}
It's kind of ugly but I'll optimize it once I have this 2nd part working. Here's how the ListBox is being loaded in the view:
<%: Html.ListBox("Skills", Model.SkillList, new { #style = "width:157px; height:90px;background:#e2f0f1;", #size = "3", #class = "inputbox" })%>
Which comes from RegistrationModelView.cs, here's SkillList in my model view:
private MultiSelectList GetSkills(string[] selectedValues)
{
List<Skill> list = new List<Skill>();
IRepository<Skill> skills = ObjectFactory.GetInstance<IRepository<Skill>>();
foreach (Skill skill in skills.GetAll())
{
list.Add(new Skill()
{
Key = skill.Key,
Name = skill.Name,
Description = ""
});
}
return new MultiSelectList(list, "Key", "Name", selectedValues);
}
And the action in AccountController.cs that loads the view
[MoveFormsScript]
public ActionResult Index()
{
return View(new RegistrationModelView());
}
I'm pretty sure all the code I posted (other than how the new item is added with the WCF service and the jQuery for consuming said service is irrelevant but I thought I'd offer as much information as possible).
Like I said the new value is added to the database no problem, my issue is updating the ListBox to reflect the new values. Anyone got any ideas and can help with this?
Well I mucked around until I found something that does what I need it to do. It may not be the most efficient or elegant way to accomplish the task but it at least works (Maybe someone will come along with a different solution some day).
What I ended up doing was make another $,ajax() call in the success of the first call like this
$("#AddNewSkill").click(function () {
$("#AddNewSkill").attr("disabled", true);
$("#newSkill").attr("disabled", true);
var data = { name: $("#newSkill").val(), description: "", type: "skill" };
data = JSON.stringify(data)
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "../WeddingPhotographerService.svc/AddNew",
data: data,
dataType: "json",
success: function () {
$.ajax({
type:"POST",
datatype:"json",
url: "../Account/GetSkills",
success:updateSkillsListBox
});
},
error: function (msg) {
alert(msg.d);
}
});
});
function updateSkillsListBox(data, status) {
$("#Skills").html("");
for(var d in data) {
$("<option value=\"" + data[d].Value + "\">" + data[d].Name + "</option>").appendTo("#Skills");
}