使用来自服务器和 ko.mapping 创建函数的元数据处理任意选项 [英] Handling arbitrary options with metadata from server and ko.mapping create function

查看:15
本文介绍了使用来自服务器和 ko.mapping 创建函数的元数据处理任意选项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个视图模型,其中一些可供选择的属性值由其他属性决定,这是通过 requires 字段设置的:

I have a view-model where some properties values available for selection are dictated by other properties, this is set via the requires field:

var clusterOptions = [{
    name: "None",
    sku: "0",
    price: 0,
}, {
    name: "Standard MySQL Cluster",
    sku: "4101",
    requires: ["MySQL1"],
    price: 10,
}, {
    name: "Enterprise MS SQL Cluster",
    sku: "4102",
    requires: ["402"],
    price: 5,
}, {
    name: "NoSQL Sharding",
    sku: "4103",
    requires: ["403","404"],
    price: 10,
}];

代码有效(您可以看到单击不同选项如何更改依赖选项databasedatabase clustering):http://jsfiddle.net/g18c/DTdyM/

The code works (you can see how clicking different options changes dependent options database and database clustering): http://jsfiddle.net/g18c/DTdyM/

此代码是静态类型的,我正在尝试对其进行转换,以便可以从使用 ko.mapping 创建的服务器视图模型发送任意数据(并需要映射).

This code is statically typed, and i am trying to convert this so that arbitrary data (and requires mappings) can be sent from the server viewmodel created with ko.mapping.

计算可用选项的代码位于辅助函数中(下面显示的示例来自我最初的静态定义示例中的两个相关属性):

The code to calculate available options for selection is in a helper function (example shown below for two dependent properties from my initial statically defined example):

self.availableDatabases = myutils.ko.createComputedDepdency(this.selectedOs, this.dbOptions);
self.availableClusteringOptions = myutils.ko.createComputedDepdency(this.selectedDb, this.dbClusteringOptions);

我已经重写了我的 serverData 并且我唯一需要从我的模型中知道的是从服务器动态传入的数组的选定项,在这种情况下它是选项数组:selectedServerOptionselectedOsOptionselectedDatabaseOptionselectedClusterOption

I have rewritten my serverData and the only things i need to know form my model are the selected items of the arrays passed in dynamically from the server, in this case it is the options array: selectedServerOption, selectedOsOption, selectedDatabaseOption and selectedClusterOption

我在处理映射时遇到困难,不确定如何使用 requiresMapping 数组.

I am stuck handling the mapping and unsure how to work with the requiresMapping array.

如何处理下面选项字段的映射?

How can I handle the mapping of the option fields below?

var serverData = {
    options: [serverOptions, osOptions, databaseOptions, clusterOptions],

    requiresMappings: [
        {target: "selectedOs", options: "dbOptions"},
        {target: "selectedDb", options: "dbClusteringOptions"}
    ]
}

var mappingScheme = {
    'options' : {
        create: function(options){
            console.log("creating sku: " + options.data.sku);
            // 1) create dependency using requiresMappings property
            // myutils.ko.createComputedDepdency(this.selectedOs, this.dbOptions);

            // 2) subscribe to updates
            // self.availableDatabases.subscribe(function () {self.selectedDb(self.availableDatabases()[0].sku);});
        }
    },
    // ignore these mappings we don't want to observe them, they are used to define mappings for the creation function above
    'ignore' : ["requiresMappings"]
}

var viewModel = ko.mapping.fromJS(serverData, mappingScheme);

我目前的小提琴在这里:http://jsfiddle.net/g18c/DTdyM/5/

推荐答案

审核后 在淘汰赛中自动映射依赖属性 答案和来自@PW Kad 的代码,编写一个简单的自定义映射器是有意义的.

After reviewing Automate mapping of dependent properties in knockout answer and the code from @PW Kad, it made sense to write a simple custom mapper.

我选择在 ko.mapping 上编写自己的映射器,因为我不知道如何引用需要的映射(使用 ko.mapping),特别是如何从 create 中创建多个属性 映射函数(如我所见,我只能返回一个新属性,而不是我需要的多个).

I chose to write my own mapper over ko.mapping as i couldn't work out how to reference the mapping for the requires (with ko.mapping), and in particular how to make multiple properties from the create mapping function (as i could see i could only return a single new property, not multiple as i would need).

它看起来运行良好,但希望能提供评论或使用 ko.mapping 的替代方法来帮助我理解,尤其是如果我重新发明了轮子!

It works seemingly well, but would appreciate comments or an alternative using ko.mapping just to help my understanding especially if i have reinvented the wheel!

小提琴在这里:http://jsfiddle.net/g18c/DTdyM/26/

var myutils = myutils || {}; // if namespace is not defined, make it equal to an empty object

myutils.ko = (function(){
    var createComputedDepdency = function(targetDependency, options){
        var computedProperty = ko.computed(function(){
            var targetValue = targetDependency();

            if(typeof targetValue === "undefined")
                return [];

            return ko.utils.arrayFilter(options, function(opt){
                if(typeof opt.requires === "undefined")
                    return true;
                else
                    return opt.requires && opt.requires.indexOf(targetValue) > -1;
            });
        });        
        return computedProperty;
    };

    var createProductViewModel = function(options){

        var viewModel = {};      
        var length = options.length; 

        console.log(length);

        for (var i = 0; i < length; i++) {
            var option = options[i];

            console.log("creating property: " + option.selectedName + ", options: " + option.optionsName);

            // create the property for selected value, i.e. object.firstName = ko.observable();
            viewModel[option.selectedName] = ko.observable();

            if(option.requires)
            {
                var computedOptions = createComputedDepdency(viewModel[option.requires.target],option.data);
                viewModel[option.optionsName] = computedOptions;

                console.log("making callback scope object for: " + option.optionsName );
                var callbackScope = {
                    callbackName: option.optionsName,
                    options: computedOptions,
                    selectedValue: viewModel[option.selectedName]
                };

                // when the list of available options changes, set the selected property to the first option
                computedOptions.subscribe(function () {
                    var scope = this;
                    console.log("my object: %o", scope);   
                    scope.selectedValue(scope.options()[0].sku);
                    console.log("in subscribe function for..." + scope.callbackName);
                },callbackScope);
            }
            else
            {
                // create the property holding values, i.e. object.nameOptions = serverData.names;
                viewModel[option.optionsName] = option.data; 
            } 
        }

        // now all options have been created, loop through the array one last time and set the dependent options.
        // note that this should be done last, as dependent calculated properties will subscribed to these events
        // and update their default first options
        for (var x = 0; x < length; x++) {
            var option = options[x];

            // only need to do this to non-calculated values
            if(!option.requires)
            {
                viewModel[option.selectedName](viewModel[option.optionsName][0].sku);
            }
        }

        return viewModel;
    };

    return{
        createProductViewModel: createProductViewModel
    };
}());

var serverOptions = [{
    name: "DELL R210",
    price: 100,
    sku: 1001,
},{
    name: "DELL R710",
    price: 200,
    sku: 1002,
},{
    name: "DELL R720 Dual CPU",
    price: 300,
    sku: 1003,
}];

var osOptions = [{
    name: "Windows Standard",
    sku: "201",
    price: 1,
}, {
    name: "Windows Enterprise",
    sku: "202",
    price: 2,
}, {
    name: "CentOS",
    sku: "203",
    price: 0,
}, {
    name: "Debian",
    sku: "204",
    price: 4,
}];

var databaseOptions = [{
    name: "None",
    sku: "0",
    price: 0,
}, {
    name: "SQL Express",
    sku: "401",
    requires: ["201", "202"],
    price: 10,
}, {
    name: "SQL Standard",
    sku: "402",
    requires: ["202"],
    price: 5,
}, {
    name: "MySQL",
    sku: "MySQL1",
    requires: ["201", "202", "203"],
    price: 11,
}, {
    name: "RavenDb",
    sku: "403",
    requires: ["203"],
    price: 12,
}, {
    name: "MongoDB",
    sku: "404",
    requires: ["204"],
    price: 13,
}];

var databaseClusterOptions = [{
    name: "None",
    sku: "0",
    price: 0,
}, {
    name: "Standard MySQL Cluster",
    sku: "4101",
    requires: ["MySQL1"],
    price: 10,
}, {
    name: "Enterprise MS SQL Cluster",
    sku: "4102",
    requires: ["402"],
    price: 5,
}, {
    name: "NoSQL Sharding",
    sku: "4103",
    requires: ["403","404"],
    price: 10,
}];

var serverData = {
    options: [
        { data: serverOptions, selectedName: "selectedServer", optionsName: "serverOptions" },
        { data: osOptions, selectedName: "selectedOs", optionsName: "osOptions" },
        { data: databaseOptions, selectedName: "selectedDb", optionsName: "availableDatabases", requires: { target: "selectedOs" } },
        { data: databaseClusterOptions, selectedName: "selectedDbCluster", optionsName: "availableClusteringOptions", requires: { target: "selectedDb" } }
    ]
};

var viewModel = myutils.ko.createProductViewModel(serverData.options);

ko.applyBindings(viewModel);

这篇关于使用来自服务器和 ko.mapping 创建函数的元数据处理任意选项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆