即时保存属性

时间:2013-12-16 09:42:04

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

我正在ASP.NET MVC网站上为用户创建Profile。我有很多属性(复选框,下拉列表等),如:

  • 时区
  • DST
  • 电子邮件设置

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

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. 点击保存按钮
  3. 但我希望他们只需点击一下即可。就像在这个截图上一样:

    enter image description here

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

    • 仅限jQuery

    • Knockout.js

    但我无法决定什么是更好的。在jQuery中,我可以直接使用DOM进行操作。但似乎淘汰赛可能更优雅,我可以使我的代码更易于维护。

    我不知道如何使用knockout执行此操作,可能需要更新元素时​​需要某种绑定,如下所示:

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

    ???

    也许有其他方法可以做到这一点。任何帮助都将受到高度赞赏。

3 个答案:

答案 0 :(得分:3)

我认为它可以用knockout + jQuery实现。使用淘汰赛非常容易构建具有不断变化布局的应用程序(如您的情况)。在您的情况下,JQuery是异步请求所必需的。

所以,viewmodel:

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 :(得分:1)

好的,你的模型必须是这样的。

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/

祝你好运!

答案 2 :(得分:1)

我的理解是您拥有的用户配置列表将被呈现为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函数只需使用ViewModel(仅一行代码)将绑定的mapping plugin再次转换为JSON字符串
  5. 最后发出另一个AJAX请求来保存更改。
  6. 要显示“已保存更改”消息,您只需传递对调用输入的引用,并使用一点点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.
            }
        });
    }