【问题标题】:Saving properties on the fly即时保存属性
【发布时间】:2013-12-16 09:42:03
【问题描述】:

我正在为我的 ASP.NET MVC 网站上的用户编写配置文件。我有很多属性(复选框、下拉菜单等),例如:

  • 时区
  • 夏令时
  • 电子邮件设置

我可以制作自己的模型来处理所有这些问题:

public class ProfileViewModel
{
    public TimeZoneOptions TimeZone { get; set; }
    public DstOptions Dst { get; set; } // Daylight Saving Time
    public bool EmailSettings1 { get; set; }
    public bool EmailSettings2 { get; set; }
    // ...
    public bool EmailSettings22 { get; set; }
}

我的视图模型属性没有相互关联。所以,我不需要打电话给ModelState.IsValid。看来我的模型将永远有效。

如果我按照在 ASP.NET MVC 中保存模型的标准方式,它将迫使我的客户点击两次:

  1. 更改属性
  2. 点击保存按钮

但我希望他们只需单击一下即可。就像这张截图一样:

我希望每个属性在用户点击它们时都被标记为“已保存更改”我想用最少的代码来实现这一点。看起来很容易做到:

  • 仅限 jQuery

  • Knockout.js

但我无法决定哪个更好。在 jQuery 中,我可以直接使用 DOM 进行操作。但似乎通过淘汰赛它可能会更优雅,并且我可以使我的代码更易于维护。

我不知道如何使用淘汰赛来做到这一点,可能是在保存元素时需要某种绑定来更新元素:

<span data-bind="display-when-saved-for-property: EmailSettings1"></span>

???

也许还有其他方法可以做到这一点。任何帮助将不胜感激。

【问题讨论】:

  • 只需为您的复选框设置一个 onchange 事件,该事件对您的控制器进行 ajax 调用。您的控制器将保存数据,ajax 成功回调将显示更改保存的消息。我就是这样做的。如果您想了解更多详情,请告诉我
  • @heymega,谢谢。以及如何检测哪些属性已更改?我必须将整个模型提交给控制器,对吗?所以它是唯一的 ajax url 来保存所有属性的模型。如何显示特定属性的“已保存更改”?
  • 给我 10 分钟,我正在为你写一些代码

标签: jquery asp.net-mvc knockout.js


【解决方案1】:

我觉得可以用knockout + jQuery来实现。使用淘汰赛非常容易构建具有更改布局的应用程序(如您的情况)。在您的情况下,JQuery 是异步请求所必需的。

所以,视图模型:

var ViewModel = function(data) {
    var self = this;
    var _isCurrentSavingProp = null;

    self.EmailSettings1 = ko.observable(data.EmailSettings1);
    self.IsEmailSettings1Saved = ko.observable();
    ... another props

    self.EmailSettings1.subscribe(function(val) {            
        isCurrentSavingProp = self.IsEmailSettings1Saved;
        self.save();
    });
    ... another subscribers

    self.save = function() {
        isCurrentSavingProp(false);
        $.post(url, ko.toJSON(self), function() {
            isCurrentSavingProp(true);
        })
    }  
}

HTML:

<span data-bind="visible: IsEmailSettings1Saved" style="display: none"></span>
<input type="checkbox" data-bind="value: EmailSettings1" />
... etc.. 

【讨论】:

  • +1,但在这种情况下,我们必须为每个属性编写至少 5 行代码。如果我的数量很少也没关系。但是如果我有很多呢?如果我有很多表格,而不仅仅是这个怎么办?我的代码很快就会变得无法维护。所以我不能接受这个作为解决方案,对不起。我认为它可以通过某种绑定来完成,但我不确定它是否可能。
  • 我为您当前的实施建议了一个解决方案。如果您问如何改进您的解决方案,我建议不要为每个设置创建一个布尔字段,而是将它们存储在数组中。在这种情况下,服务器和客户端代码大小将被最小化。
  • 哦不,我不是在谈论客户端和服务器之间传输的字节数。这是关于可维护性的。如果我有很多属性,我必须手动为每个属性编写“isCurrentSavingProp = self.IsEmailSettings1Saved”。效果很差,必须优化。
  • 我也不关心数据传输。我建议您优化代码的大小 - 使用数组而不是单个字段。使用数组,您可以轻松设置视图中的数据(迭代数组),初始化剔除视图模型,并保存修改后的数据。
【解决方案2】:

我的理解是,您拥有将呈现为checkboxdropdownlist 的用户配置列表。
如果是这样,那么您是对的,KnockoutJS 是一种更优雅的方式(我之前遇到过同样的问题,除了我有一个保存按钮,我很幸运)。让我们看看这里有什么:
您的模型主要具有bool 属性,那么毫无疑问您应该使用KnockoutJS mapping plugin。为什么?我们要做的只是:

  1. 正常编写view HTML 标记。
  2. 对于我们view的第一次加载,发出AJAX请求以获取用户配置文件,然后使用mapping plugin将返回的数据转换为ViewModel并将其绑定到我们的view(所有这些都不需要我们自己编写 ViewModel,这意味着如果服务器上的 ViewModel 发生变化,我们只需要修改 view)。
  3. 为每个checkboxdropdownlist 添加change 事件处理程序(使用jQuery)。
  4. change 函数只需使用 mapping plugin 再次将绑定的 ViewModel 转换为 JSON 字符串(只需一行代码)
  5. 终于发出另一个 AJAX 请求来保存更改。

要显示“已保存更改”消息,您可以简单地传递调用输入的引用并使用一点 jQuery 魔术附加您的消息。

更新:
你的可能应该是这样的(或者可能比我的更好):

var UserProfile;
$(document).ready(function() {
    $('#ViewContainer input:checkbox').change(function (event) {
        SaveUserProfile(event);
    });
    IntializeUserProfile();
});

function IntializeUserProfile() {
    // Show spinner
    $.ajax({
        type: "POST",
        url: URL_To_Get_User_Profile,
        data: UserId, // in case of you need to pass data.
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function(data) {
            UserProfile = ko.mapping.fromJSON(data); //ko.mapping.fromJS($.parseJSON(msg));
            ko.applyBindings(UserProfile, selector);
            // Hide Spinner
        }
    });
}

function SaveUserProfile(event) {
    // Show Spinner
    var model = ko.toJSON(ko.mapping.toJS(UserProfile));
    event = event || window.event;
    target = event.target || event.srcElement;
    $.ajax({
        type: "POST",
        url: URL_To_Save_UserProfile,
        data: model,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function(data) {
            // Hide Spinner

            // here you have the caller input ==>target.
            // for example, you can get it's parent to append your message
            // or just show message in the page as notification.
        }
    });
}

【讨论】:

  • 谢谢,这正是我认为它应该工作的方式。但!如果我使用映射插件,如何传递调用输入的引用?有没有办法自动做到这一点?以及如何仅在成功的情况下显示“已保存的更改”?
【解决方案3】:

好的,你的模型需要是这样的。

public class MyModel
{

    public bool? Friends { get; set; }
    public bool? Photos { get; set; }
    public string Car { get; set; }

}

您的控制器方法接收模型的一个实例。只要您的 ajax 请求的数据名称与模型中的属性名称匹配,它就会绑定。

    public void UpdateValues(MyModel model)
    {

        if(model.Friends != null)
        {
            //Save
        }
        if (model.Photos != null)
        { 
            //Save
        }
        if (model.Car != null)
        { 
            //Save
        }


    }

假设你的 html 标记是这样的

<div id="checkbox-container">
    <div>    
        <label>
            <input type="checkbox" name="friends" />
        My Friends</label>
    </div>
    <div>
        <label><input type="checkbox" name="photos" />My Photos</label>
    </div>
    <select id="car-dropdown">
      <option value="volvo">Volvo</option>
      <option value="saab">Saab</option>
      <option value="mercedes">Mercedes</option>
      <option value="audi">Audi</option>
    </select> 
</div>

这里是 jQuery....

$('#checkbox-container input:checkbox').change(function () {
    //Gets the property name we will bind
    var field = $(this).attr("name");
    var isChecked = $(this).prop("checked");

    var data = {};
    data[field] = isChecked;

    requestBuilder(data);

 });

//New method for our dropdown
$('#car-dropdown').change(function()
{


    var data = {};
    data['Car'] = $(this).val();

    requestBuilder(data);


});


function requestBuilder(data)
{

    //Ajax Request
    $.ajax({
    //Change url to match your controller
    url:"/Controller/UpdateValues/",
    data:data,
    type:"POST",
    success:function(response)
    {
       alert("Change Saved")
    }
    });

 }

注意我们是如何传递 Name="" 属性的。这与我们模型中的名称匹配,因此它将绑定:)

如果您需要我扩展任何元素,请告诉我。我还为前端代码创建了一个 jsfiddle - http://jsfiddle.net/8tKBx/

祝你好运!

【讨论】:

  • 好的,谢谢!现在我希望在单击的属性附近显示“已保存的更改”。例如,如果我单击“我的朋友”,我希望在此元素之后立即显示“已保存的更改”。但只有在成功的情况下。如果我有下拉列表怎么办?我认为我们不能只发送一个属性,将整个模型本身发送到服务器会更容易。
  • 有几种不同的方法可以做到这一点。这是我进行的一项相当大的更改,但您可以进行实验并自己制作。 jsfiddle.net/8tKBx/4 。我将编辑我的原始答案以包含一个下拉列表。给我5分钟
猜你喜欢
  • 2016-09-18
  • 2020-12-30
  • 1970-01-01
  • 1970-01-01
  • 2022-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多