Knockout JS创建一个脏标志

时间:2014-09-26 04:15:30

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

我有一个ASP.NET MVC项目,我将我的IEnumerable类型转换为JS字符串并存储在隐藏的输入中。然后我在applyBindings时将该字符串解析为viewModel对象,该对象是data-bind =" foreach:rows"在元素上绑定。该系统是我工作的内部开发的MVC框架的一部分,需要构建为易于重用的系统。主要设计目标是使用代码来构建具有驱动布局的属性的.NET ViewModel。我试图阻止开发人员编写JS视图模型来匹配。

我在一个用于初始化绑定的示例项目中的JS如下:

var thingy = function(rowData){
    rows = ko.mapping.fromJS(rowData);
};


var el1 = $("#someGrid > tbody");
if (el1.length > 0) {
    var dataStr = el1.attr("data-ko-data");
    var data = JSON.parse(dataStr);
    var vm = thingy(data);

    ko.applyBindings(vm, el1[0]);

}

我的.NET POCO看起来像这样:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; } 
    public string Surname { get; set; }
    public int Age { get; set; }
    public bool IsDirty { get; set; }

    public Tuple<string, Type>[] Properties()
    {
        var propInfos = this.GetType().GetProperties();
        var ret = propInfos.Select(i => new Tuple<string, Type>(i.Name, i.PropertyType));
        return ret.ToArray();

    }
}

表单上的每个表都有一个&#34;保存更改&#34;按钮,它应该扫描表的viewmodel并将标记为IsDirty = true的行发送回MVC Action以进行保存。

$(".gridSaveButton").click(function () {
    var theUrl = $(this).attr("data-saveurl");
    var theTable = $(this).siblings("table"); 

    var dirtyRows = [];

    var theBody = theTable.children("tbody");
    var trs = theBody.children("tr");
    trs.each(function () {
        var row = $(this);
        var modelForRow = ko.dataFor(row[0]);
        if(modelForRow.IsDirty)
        {
            dirtyRows.push(modelForRow);
        }

    });

    var theData = ko.toJSON(dirtyRows);
    $.ajax({
        url: theUrl,
        type: "POST",
        contentType: "application/json",
        data: theData,
        success: function (status) {
            alert("Success: " + status.Success);
        },
        error: function (request, status, theError) {
            alert(theError);
        }
    });

});

所以上面的工作正如预期的那样。我遇到的问题是,当修改对象的任何属性时,如何将特定行的IsDirty值设置为true。到目前为止,我在线找到的每个例子都处理将IsDirty设置为基于某些其他ko.observable的ko.computed,但是我不能直接这样做,因为用户必须编写一个显式的JS每种类型的viewmodel。

我尝试创建一个自定义绑定,但init和update都没有被调用,所以我对此有点失落,并且就此而言,如果它甚至是正确的方法。

有没有人对这类事情有任何经验,并且知道当对象的声明是隐式的时候我怎么能简单地响应对象的任何其他变量?

- 史蒂夫

2 个答案:

答案 0 :(得分:0)

好的,所以我已经将这个样本用作基础并使其运行良好。 http://jsfiddle.net/rniemeyer/7Nsuh/

这是一个修改版本,以满足我的需要,我正在使用ko.mapping.fromJS,虽然来自Ryan的原始版本http://jsfiddle.net/rniemeyer/dtpfv/,viewModel上有一个dirtyItems列表。有了修改过的方法,有没有人知道如何将计算出的dirtyItems添加到koRowMapping?

我尝试了这个,但绑定失败了,我甚至不确定这种方法是否正确:

var koRowMapping = {
    rows: {
        create: function (options) {
            return createRow(options.data);
        }
    },
    dirtyItems: ko.computed(function() {
        return ko.utils.arrayFilter(this.rows, function(row) {
            return row.dirtyFlag.isDirty();
        });
    }, this)
};

答案 1 :(得分:0)

Roighto !!

我最终到了那里。

所以它就像Ryan最初的例子一样:

var koRowMapping = {
    rows: {
        create: function (options) {
            return createRow(options.data);
        }
    }

};

var createRow = function (row) {
    var result = ko.mapping.fromJS(row);
    result.dirtyFlag = ko.dirtyFlag(result);
    return result;
}

ko.dirtyFlag = function (root, isInitiallyDirty) {
    var result = function () { },
        _initialState = ko.observable(ko.toJSON(root)),
        _isInitiallyDirty = ko.observable(isInitiallyDirty);

    result.isDirty = ko.computed(function () {
        return _isInitiallyDirty() || _initialState() !== ko.toJSON(root);
    });

    result.reset = function () {
        _initialState(ko.toJSON(root));
        _isInitiallyDirty(false);
    };

    return result;
};

然后将dirtyItems和全局脏标志放在viewmodel上:

$(document).ready(function () {
    var people = '{"rows":[{"FirstName":"Steve","LastName":"O"},{"FirstName":"Someone","LastName":"Else"}]}';

    var vm = ko.mapping.fromJSON(people, koRowMapping, {});
    vm.dirtyItems = ko.computed(function() {
        return ko.utils.arrayFilter(this.rows(), function(item) {
            return item.dirtyFlag.isDirty();
        });
    }, vm);

    vm.isDirty = ko.computed(function () {
        return this.dirtyItems().length > 0;
    }, vm);


    var el = $("#theTable");
    ko.applyBindings(vm, el[0]);
}

绝佳!!!!