使用ajax将Knockout.js viewmodel发布到ASP.NET MVC Controller操作会导致空值

时间:2015-12-09 13:22:40

标签: c# jquery ajax asp.net-mvc knockout.js

我正在为网站构建一个简单的CMS。该网站使用asp.net mvc构建,我使用knockout.js进行双向绑定。我可以将数据库中的值发送到我的视图并更改模型值。当我尝试保存修改后的viewmodel时出现问题。 我使用以下类将数据保存在DB中:

public class Content
{
    public int ContentId { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
}

我的完整视图模型:

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);
            }
        }
    }
}

我的保存功能(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);
                }
            }
        });
    }
}

最后,我的保存操作方法:

    [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);
    }

更新

将被发送到服务器的JSON模型。我找不到与我的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
}

编辑问题

我遇到的问题是,当我尝试发布到控制器时,所有值都为空。 ajax命中正确的Action,我发现我发布的JSON模型和我的C#viewmodel之间没有任何区别。

感谢任何帮助!

Martin Johansson

3 个答案:

答案 0 :(得分:3)

首先:您的ContentType未完整!

第二:您将数据映射到self,这是正确的,然后您将方法save()分配给self并通过邮寄方式将其作为数据发送,所以你也发布了save()

试试这个:

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);
                }

            }
        });
    }
}

希望它有所帮助!

答案 1 :(得分:1)

您的视图模型中有一些逻辑。我个人不推荐它。如果您使用构造函数以这种方式设置值,那么您实际上限制了将集合等内容作为属性的能力。所以,让我们把这个问题放在一边,找出解决方案。

  1. 不要使用视图模型的构造函数来填充单个属性。如果必须以这种方式填充值,请使用单独的公共方法。
  2. 现在你有一个空的构造函数。初始化后,请确保调用公共方法来填充初始数据。
  3. 现在发布时,如果你的淘汰视图模型与你的C#viewModel具有匹配的属性,你应该在SaveViewModel方法中收到一个正确填充的视图模型实例。

答案 2 :(得分:0)

当您想要创建类的实例时,无法省略带参数的构造函数(除了使用静态原型和使用MemberwiseClone克隆对象,但在您的解决方案中,它需要构建自定义模型绑定器并手动解析所有数据)

如果添加了无参数构造函数,并且在发布模型属性为空之后,这意味着绑定器无法正确地将数据从请求绑定到模型。你应该仔细检查从浏览器到服务器的实际情况。 尝试将您的ajax请求更改为(JSON.stringify部分很重要)

contentType: 'application/json; charset=utf-8', data: JSON.stringify(model)

相关问题