【问题标题】:How to add parent function to KnockOut double-nested mapping?如何将父函数添加到 KnockOut 双嵌套映射?
【发布时间】:2017-02-19 08:46:42
【问题描述】:

我使用 KnockOut 映射 fromJS 从 JSON 对象创建我的视图模型,如下所示:

{
    "cats": [{
        "name": "fluffy",
        "color": "brown",
        "kittens": [{
            "name": "spot",
            "color": "brown"
        }, {
            "name": "rascal",
            "color": "grey"
        }, {
            "name": "trouble",
            "color": "white"
        }]
    }, {
        "name": "kitty",
        "color": "red",
        "kittens": [{
            "name": "lady",
            "color": "red"
        }, {
            "name": "skat",
            "color": "striped"
        }]
    }]
}

html:

<div data-bind="foreach:cats">
    <span data-bind="text:name"></span>
    <table>
        <tr data-bind="foreach: kittens">
             <td data-bind="text:name"></td>
             <td data-bind="text:color"></td>
             <td><a data-bind="click: $parent:showParentColor" href="#">Parent Color</a></td>
        </tr>
    </table>
</div>

Javascript:

var KittenModel = function (data) {
    ko.mapping.fromJS(data, {}, this);

    // ... various computed values added to this
}

var mapping = {
    'kittens': {
        create: function(options) {
            return new KittenModel(options.data);
        }
    },
    'otherItem': {
        create: function(options) {
             return ('otherStuff');
        }
     }
}

var data = { ... }; // the JSON above

var CatsViewModel = ko.mapping.fromJS(data, mapping);

问题:

如何以及如何放置 showParentColor() 函数以便数据绑定在 kitten 表中起作用?例如:

function showParentColor(cat) {
    alert(cat.color);
}

谢谢!

【问题讨论】:

    标签: knockout.js knockout-mapping-plugin


    【解决方案1】:

    您可以根据您的视图模型层次结构使用以下之一:

    • $root :这指向根上下文中的主视图模型对象。最顶层的父上下文。
    • $parents 数组:这是一个包含所有视图模型的数组。

      • $parents[0] :父视图模型上下文。(也和$parent一样)

      • $parents[1]:第二个父视图模型上下文。(祖父)

      • $parents[2]: 第三个父视图模型上下文。 (曾祖父母)

      • 等等....


    更新: 如果你想在CatsViewModel 级别添加一个函数,你只需将你的函数添加到创建的模型中。

    示例:https://jsfiddle.net/kyr6w2x3/87/

    JS:

     CatsViewModel.showParentColor = function(item){
          console.log(item.name());
          console.log(item.color());
        }
    

    查看:

    <a data-bind="click: $parents[1].showParentColor">
    

    下面是模型的层次结构

    - CatsViewModel 
        - cats : observableArray
            - name : observable
            - color : observable
            - kittens : observableArray
                   - name : observable
                   - color : observable
        - showParentColor : function
    


    替代方案:您可以自己完成工作并创建模型。您可以根据需要更轻松地修改和维护。您还可以在所需的任何模型中添加click 函数。

    例如:http://jsfiddle.net/kyr6w2x3/91/

    HTML:

    <div data-bind="foreach:cats">
        <span data-bind="text:name"></span>
        <table>
        <tbody data-bind="foreach: kittens">
           <tr>
                 <td data-bind="text:name"></td>
                 <td data-bind="text:color"></td>
                 <td><a data-bind="click: $parent.showParentColor" href="#">Parent Color</a></td>
            </tr>
        </tbody>
    
        </table>
    </div>
    

    JS:

     var data = {
        "cats": [{
            "name": "fluffy",
            "color": "brown",
            "kittens": [{
                "name": "spot",
                "color": "brown"
            }, {
                "name": "rascal",
                "color": "grey"
            }, {
                "name": "trouble",
                "color": "white"
            }]
        }, {
            "name": "kitty",
            "color": "red",
            "kittens": [{
                "name": "lady",
                "color": "red"
            }, {
                "name": "skat",
                "color": "striped"
            }]
        }]
    }
    
    var CatsViewModel = function (data){ 
      var self = this;
      self.cats = ko.observableArray($.map(data.cats, function (item) {
            return new CatItemViewModel(item);
        }));
    }
    var CatItemViewModel = function (data){
      var self = this;
      self.name = ko.observable(data.name);
      self.color = ko.observable(data.color);
      self.kittens = ko.observableArray($.map(data.kittens, function (item)     {
       var newData = Object.assign({}, item, { parent: self.name()});
            return new KittenModel(newData);
       }));
       self.showParentColor = function (item){
          console.log("Parent Name: " , self.name());
          console.log("Name: " , item.name());
          console.log("Color: " , item.color());
       }
    }
    var KittenModel = function (data) {
      var self = this;
      self.name = ko.observable(data.name);
      self.color = ko.observable(data.color);
      self.parent = ko.observable(data.parent);
    }
    var vm = new CatsViewModel(data);
    ko.applyBindings(vm);
    

    【讨论】:

    • 谢谢。我在示例中使用了 $parent(或 $parents[0]),因为我想从“kitten”对象调用“cat”对象上的函数。我不明白的是如何通过映射参数将该函数放入“猫”对象中。
    • 所以我试过了,但不明白为什么将函数添加到 CatsViewModel 需要我用 $parents[1] (或 $root)而不是 $parents[0] (或$父)。我想是小猫的父母,而不是祖父母!你能解释一下吗?
    • 感谢您的示例。但是,它记录的是小猫而不是猫的名字和颜色! (如果猫和小猫有不同的属性,这会更明显。)其次,如果我们将方法添加到 CatsViewModel,它如何知道引用了哪只猫?
    • 看看更新的例子。如果您的猫的名字是unique,您可以在映射时将父级添加到kittens 数据中,然后在点击函数中您知道它是哪个父级。
    • 那么 确实 工作(类似于 user3297291 的替代方案 2),但是有没有办法为每个可以在“小猫”中引用的“猫”对象添加一个方法' $parent 的对象?我认为这就是在 KnockOut foreach 循环中使用 $parent、$parents[] 和 $root 属性的全部意义所在。再次感谢您抽出宝贵时间!
    【解决方案2】:

    注意:我喜欢其他答案的方法比这个更好,但确实想提出一种替代解决方案,以便有多种选择。

    备选方案 1

    您可以在 KittenModel 中创建一个“静态”方法来记录它传递的任何猫的颜色:

    var KittenModel = function (data) {
      ko.mapping.fromJS(data, {}, this);
    };
    
    KittenModel.logCatColor = function(cat) {
      console.log(cat.color);
    };
    

    现在,因为您的视图可以访问父子结构,您可以使用任何您想要的父级调用此方法:

    <!-- to log the parent's color -->
    <div data-bind="click: logCatColor.bind(null, $parent)"></div>
    
    <!-- knockout automatically passes `$data` as a first argument,
         so you won't need to bind the method to log your own color -->
    <div data-bind="click: logCatColor"></div>
    

    一个例子:

    ko.applyBindings({
      name: "parent",
      child: {
        name: "child",
        logName: function(entity) {
          console.log(entity.name);
        }
      }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    <h1 data-bind="text: name"></h1>
    <div data-bind="with: child" style="border: 1px solid black">
      <h2 data-bind="text: name">
        </h2>
      <button data-bind="click: logName">
        log my name
      </button>
      <button data-bind="click: logName.bind(null, $parent)">
        log my parent's name
      </button>
    </div>

    备选方案 2

    在作为选项传递的工厂函数中,dataparent 都可用:options.data 保存当前正在映射的项目。在这种情况下,options.parent 将引用父级cat。我没有对此进行测试,但这可能有效:

    'kittens': {
        create: function(options) {
            var dataWithParent = Object.assign({}, 
                                   options.data, 
                                   { parent: options.parent });
    
            return new KittenModel(dataWithParent);
        }
    },
    

    【讨论】:

    • 谢谢。有效的有趣方法。直到现在我还没有看到绑定方法。 Per Mozzilla:“bind() 方法创建了一个新函数,在调用该函数时,它的 this 关键字设置为提供的值,在调用新函数时,给定的参数序列在任何提供的参数之前。”但是,将 'cat' 传递给 'kitten' 函数对我来说并不合适,因为小猫应该“意识到”它的父母,不是吗?
    • 我想这取决于你是否需要一只小猫来了解它的父母......你可以说CatsViewModel负责CatModelKittenModel实例之间的关系。将这些引用存储在模型中将是这些数据的重复。 然而,在实践中,轻松接触父母和孩子当然是非常有用的。
    • @ScottyB 我提供了第二个替代方案,它可能更接近映射库和您的最初意图。
    猜你喜欢
    • 2012-09-24
    • 1970-01-01
    • 2012-08-24
    • 2012-06-24
    • 2015-03-17
    • 1970-01-01
    • 1970-01-01
    • 2011-10-04
    • 2020-08-29
    相关资源
    最近更新 更多