Reputation: 798
I'm building a simple CMS for a website. The site is built with asp.net mvc and I'm using knockout.js for two-way binding. I can send my values from DB to my view and change the model values. My problem comes when I try to save the modified viewmodel. I'm saving the data in DB using the following class:
public class Content
{
public int ContentId { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
My full viewmodel:
public class CmsStartPageViewModel
{
public string SectionOneTitle {get; set;}
public string SectionOneText {get; set;}
public string SectionTwoTitle {get; set;}
public string SectionTwoText {get; set;}
public string SectionThreeTitle {get; set;}
public string SectionFourTitle {get; set;}
public string BannerOneTitle {get; set;}
public string BannerOneSubTitle {get; set;}
public string BannerOneIcon {get; set;}
public string SectionFiveTitle {get; set;}
public string SectionFiveSubTitle {get; set;}
public string ContactTitle {get; set;}
public string ContactSubTitleOne {get; set;}
public string ContactText {get; set;}
public string ContactSubTitleDetails {get; set;}
public string ContactSubTitleSocial {get; set;}
public List<SupportingCompany> SupportingCompanies { get; set; }
public List<ReasonTemplate> ReasonTemplates { get; set; }
public List<ProductInfoTemplate> ProductInfoTemplates { get; set; }
//Kontaktformulär Validering
public string FormFullName { get; set; }
public string FormEmail { get; set; }
public string FormSubject { get; set; }
public string FormMessage { get; set; }
public void PopulateViewModel(ICollection<StartContent> data)
{
var self = this;
var properties = typeof(CmsStartPageViewModel).GetProperties();
foreach (PropertyInfo property in properties)
{
var type = property.PropertyType;
if (type == typeof(String) &! property.Name.StartsWith("Form"))
{
var value = data.Single(d => d.Name == property.Name);
property.SetValue(self, value.Value);
}
}
}
}
My save-function (knockout.js viewmodel):
ViewModel = function(data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
self.save = function () {
var model = ko.mapping.toJSON(self);
$.ajax({
url: "/Cms/SaveViewModel",
type: "POST",
data: model,
dataType: "json",
contentType: "json",
success: function(message) {
ko.mapping.fromJS(data.viewModel, {}, self);
if (message.Status === "success") {
toastr.success(message.Content);
} else if (message.Status === "error") {
toastr.error(message.Content);
}
}
});
}
}
And finally, my Save action method:
[HttpPost]
public ActionResult SaveViewModel(CmsPageViewModel model)
{
var content = _contentRepo.GetStartContent();
foreach (var item in content)
{
var property = model.GetType().GetProperty(item.Name);
property.SetValue(model, item.Value);
}
_contentRepo.UpdateStartContent(content);
var message = new StatusMessageCms
{
Content = "Your changes has now been saved!",
Status = "success"
};
return Json(message);
}
The JSON-model that's getting sent to server. I can't find any mismatches with my C# Viewmodel
{
"SectionOneTitle": "Aja-Baja.se avskräcker stöld och bedrägerier",
"SectionOneText": "När vi blir tillräckligt många, kan vi stöldförsäkra till ett subventionerat pris.<br />\nIntresse från flera försäkringsbolag finns",
"SectionTwoTitle": "Om Aja-Baja.se",
"SectionTwoText": "Vi är två crossmotionärer som ser ett växande problem med stölder av motorcrosscyklar. För att förhindra detta så startar vi ett register där alla kan registrera sina crossar. <br /><br />\n\nDU vill inte köpa en stulen cross och inte heller bli bestulen på dem. Om alla som har en cross eller tänker köpa en, använder registret och sökfunktionen så hjälps vi åt att hålla koll så att det inte blir så intressant att stjäla dem.",
"SectionThreeTitle": "Varför ska du registrera dig?",
"SectionFourTitle": "Priser & Specifikationer",
"BannerOneTitle": "Samarbeta mot inbrottstjuvarna",
"BannerOneSubTitle": "REGISTRERA DIG HOS AJA-BAJA OCH HÅLL KOLL DU MED!",
"BannerOneIcon": "fa-users",
"SectionFiveTitle": "Vi är inte de enda som är entusiastiska inför Aja-Baja.se...",
"SectionFiveSubTitle": "Flera samarbetspartners tror på vår idé",
"ContactTitle": "Kontakta Oss",
"ContactSubTitleOne": "Gör oss <strong>Bättre</strong>",
"ContactText": "Det är viktigt för oss och veta vad ni som besökare tycker om vår Mobilapp och Webbapplikation. Har ni ett förslag på vad som skulle kunna göras bättre? Eller är det något som ni saknar? Fyll isåfall i formuläret och skicka det till oss. Tillsammans är vi starka!",
"ContactSubTitleDetails": "Aja-Bajas <strong>Kontaktuppgifter</strong>",
"ContactSubTitleSocial": "Sociala <strong>Medier</strong>",
"SupportingCompanies": [{
"SupportingCompanyId": 1,
"ImageUrl": "/Content/img/sponsor/alternativ_mc_sponsor.png",
"LinkUrl": "http://www.alternativ1mc.se/"
}, {
"SupportingCompanyId": 2,
"ImageUrl": "/Content/img/sponsor/eliasson_racing.png",
"LinkUrl": "http://www.eliassonracing.se/"
}, {
"SupportingCompanyId": 3,
"ImageUrl": "/Content/img/sponsor/KonicaMinolta_Logo.png",
"LinkUrl": "http://www.konicaminolta.se/sv/home.html"
}, {
"SupportingCompanyId": 4,
"ImageUrl": "/Content/img/sponsor/frisk_sponsor.png",
"LinkUrl": null
}],
"ReasonTemplates": [{
"ReasonTemplateId": 1,
"ReasonTitle": "Sökbarhet",
"ReasonText": "Möjlighet att via ramnummer se om crossen är stulen.",
"ReasonIcon": "fa-search"
}, {
"ReasonTemplateId": 2,
"ReasonTitle": "Stöldanmälning",
"ReasonText": "Anmäl snabbt din cross stulen via app eller hemsida.",
"ReasonIcon": "fa-bullhorn"
}, {
"ReasonTemplateId": 3,
"ReasonTitle": "Samarbetspartners",
"ReasonText": "Verkstäder och återförsäljare kan kontrollera om crossen är stulen.",
"ReasonIcon": "fa-users"
}, {
"ReasonTemplateId": 4,
"ReasonTitle": "Ägarbyten",
"ReasonText": "Ett smidigt och säkert sätt att registrera ett ägarbyte.",
"ReasonIcon": "fa-refresh"
}, {
"ReasonTemplateId": 6,
"ReasonTitle": "Motverka stölder",
"ReasonText": "Stöldanmälda registrerade crossar blir svårare att sälja vidare.",
"ReasonIcon": "fa-lock"
}, {
"ReasonTemplateId": 8,
"ReasonTitle": "Förmåner som kund",
"ReasonText": "Rabatter hos utvalda verkstäder och butiker.",
"ReasonIcon": "fa-user"
}],
"ProductInfoTemplates": [],
"FormFullName": null,
"FormEmail": null,
"FormSubject": null,
"FormMessage": null
}
Edited question
The issue I encounter is that when I try to post to the controller all values are null. The ajax hits the correct Action and I can't find any differences between my posted JSON-model and my C# viewmodel.
Any help is appreciated!
Martin Johansson
Upvotes: 2
Views: 5908
Reputation: 2630
First: Your ContentType is not complete!
Second: You map your data to self
, which is correct, then you assign the method save()
to self
and send it via post as data, so you are also posting save()
.
Try this:
ViewModel = function(data) {
var self = this;
var dataToPost = ko.mapping.fromJS(data, {}, self);
/*
You can pick another name for the variable dataToPost
This has to be a valid json, use a debugger or fiddler to see how it looks like
*/
self.save = function () {
var model = ko.mapping.toJSON(dataToPost);
$.ajax({
url: "/Cms/SaveViewModel",
type: "POST",
data: model,
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function(message) {
ko.mapping.fromJS(data.viewModel, {}, self);
if (message.Status === "success") {
toastr.success(message.Content);
} else if (message.Status === "error") {
toastr.error(message.Content);
}
}
});
}
}
Hope it helps!
Upvotes: 3
Reputation: 677
There is no way to omit constructor with parameters when you want to create instance of a class (apart of using static prototype and cloning object with MemberwiseClone, but in your solution it would require building custom model binder and parse all data manually).
If you have added parameterless constructor and after posting model properties are empty, that means that binder wasn't ably to properly bind data from request to your model. You should double check what actually goes from browser to server. Try to change your ajax request to (the JSON.stringify part is important)
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(model)
Upvotes: 0
Reputation: 2143
Your view model has a bit of logic in it. I personally do not recommend it. If you are using the constructor to set values this way, you are essentially limiting your ability to have things like collections etc as properties. So let's put that problem aside and figure out a solution.
SaveViewModel
method.Upvotes: 1