使用ajax将Knockout.js视图模型发布到ASP.NET MVC控制器操作会导致null值

本文关键字:操作 控制器 MVC NET null ASP Knockout ajax js 视图 模型 | 更新日期: 2023-09-27 18:26:12

我正在为一个网站构建一个简单的CMS。该网站是用asp.net mvc构建的,我使用knockout.js进行双向绑定。我可以将我的值从DB发送到我的视图,并更改模型值。当我试图保存修改后的视图模型时,问题就出现了。我使用以下类将数据保存在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 = 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#视图模型之间找不到任何区别。

感谢您的帮助!

Martin Johansson

使用ajax将Knockout.js视图模型发布到ASP.NET MVC控制器操作会导致null值

第一:您的ContentType不完整!

第二:您将数据映射到self,这是正确的,然后将方法save()分配给self,并将其作为数据通过post发送,因此您也在post 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. 不要使用视图模型的构造函数来填充各个属性。如果必须以这种方式填充值,请使用单独的公共方法
  2. 现在您有一个空的构造函数。初始化后,请确保调用公共方法来填充初始数据
  3. 现在,当您发布时,如果您的淘汰视图模型具有与C#视图模型匹配的属性,那么您应该在SaveViewModel方法中收到一个正确填充的视图模型实例

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

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

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