【问题标题】:Post Knockout.js viewmodel to ASP.NET MVC Controller action using ajax results in null-values使用 ajax 将 Knockout.js 视图模型发布到 ASP.NET MVC 控制器操作会导致空值
【发布时间】:2016-03-14 18:59:34
【问题描述】:

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

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

最后,我的 Save 操作方法:

    [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 命中了正确的操作,我找不到我发布的 JSON 模型和我的 C# 视图模型之间的任何差异。

感谢任何帮助!

马丁·约翰逊

【问题讨论】:

  • 如果你上面的代码是正确的,那么你的ViewModel实际上没有有一个无参数的构造函数:public CmsStartPageViewModel(ICollection data)
  • 我知道,我可以添加它,但正如我所说,控制器中的所有值都会变为空。
  • 你能告诉我们你是如何初始化 ViewModel 的吗?
  • 当我发布我没有初始化它时,ko.mapping.toJSON() 会处理 viewmodel 到 json 的转换,asp.net 会尝试自动将其转换为 C# viewmodel。不知道那是怎么做的,但我已经让它与另一个视图模型一起工作。不同之处在于我需要一个构造函数。
  • 你在哪里看到这个错误?是从淘汰赛方面还是从 C# 方面?

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


【解决方案1】:

第一:您的 ContentType 不完整!

第二:您将数据映射到self,这是正确的,然后您将方法save()分配给self并通过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);
                }

            }
        });
    }
}

希望对你有帮助!

【讨论】:

  • contentType: "application/json; charset=utf-8" 和 ko.mapping.toJSON(..) 是我缺少的链接。
【解决方案2】:

您的视图模型中包含一些逻辑。我个人不推荐它。如果您使用构造函数以这种方式设置值,则本质上限制了将集合等作为属性的能力。所以让我们把这个问题放在一边,想办法解决。

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

【讨论】:

  • 是的,我不应该为该操作使用构造函数,使用方法会更好。但是我仍然在所有内容上都得到空值。我将使用发送到服务器的 Json 模型和完整的视图模型来更新问题。
【解决方案3】:

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

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

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

【讨论】:

  • 将构造函数移至方法,并将从服务器发送的 JSON 模型和完整的 C# 视图模型添加到我的问题中。我认为 ko.mapping.ToJSON() 函数将模型转换为 json,因此我不必再次对其进行字符串化。
猜你喜欢
  • 2018-04-22
  • 1970-01-01
  • 1970-01-01
  • 2019-01-26
  • 1970-01-01
  • 2014-04-04
  • 1970-01-01
  • 2019-03-30
  • 2017-08-20
相关资源
最近更新 更多