【问题标题】:KnockoutJS: How to get a reference to ViewModel when clicking a bound list itemKnockoutJS:单击绑定列表项时如何获取对 ViewModel 的引用
【发布时间】:2015-07-06 12:19:29
【问题描述】:

单击绑定的 html 列表项时如何获取对根视图模型的引用?

我有一个使用 KnockoutJS 进行绑定的 ViewModel 闭包“类”。

ViewModel 拥有一个ko.observableArray,表示无序列表中的每个列表项。

// ViewModel Base
var ViewModel=( function(){

    // ctor
    function ViewModel(data,id){
        // Publics
        this.list=ko.observableArray([{name:'one'},{name:'two'},{name:'three'}]);
        this.selectedItem=ko.observable(null);
    };

    // set "selectedItem" when user clicks an <li>
    ViewModel.prototype.selectItem=function(){

        // below works when using instantiated "vm1"
        vm1.selectedItem(this);

        // correctly reports the name property of the clicked list item
        console.log(vm1.selectedItem().name);
    };

    // Return Publics
    return(ViewModel);

})();

// Build a new view model
var vm1=new ViewModel();

// apply bindings
ko.applyBindings(vm1);

observableArray 绑定到一个 Html 无序列表。

点击列表项时,每个 html 列表项都会在 ViewModel 中触发 .selectItem

<ul data-bind='foreach:list'>
    <li class='item' data-bind='click: $root.selectItem' >
        <span data-bind='text:name'></span>
    </li>
</ul>

问题是……

.selectItem 中,我需要在视图模型实例上设置selectedItem 属性。

但我看不到从 KnockoutJS 提供给 selectedItemthis 获取视图模型实例的方法。

直接使用 vm1 实例可以工作,但我不希望我的通用 ViewModel 绑定到使用对 vm1 的硬编码引用

    // set "selectedItem" when user clicks an <li>
    ViewModel.prototype.selectItem=function(){

        // below fails when using "ViewModel" (and numerous other tries by me)
        ViewModel.selectedItem(this);  // error 

        console.log(vm1.selectedItem().name);
    };

片段:

function log(){console.log.apply(console,arguments);}


// ViewModel Base
var ViewModel=( function(){

  // ctor
  function ViewModel(data,id){
    // Publics
    this.list=ko.observableArray([{name:'one'},{name:'two'},{name:'three'}]);
    this.selectedItem=ko.observable(null);
  };

  // set "selectedItem" when user clicks an <li>
  ViewModel.prototype.selectItem=function(){
    // below works when using specified vm1
    // below fails when using ViewModel.selectedItem(this)
    vm1.selectedItem(this);
    alert('Clicked on: '+vm1.selectedItem().name);
  };

  // Return Publics
  return(ViewModel);

})();

// Build a new view model
var vm1=new ViewModel();

// apply bindings
ko.applyBindings(vm1);
<p>Click on a list item.</p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<ul data-bind='foreach:list'>
  <li class='item' data-bind='click: $root.selectItem' >
    <span data-bind='text:name'></span>
  </li>
</ul>

【问题讨论】:

    标签: knockout.js


    【解决方案1】:

    The recommended way of writing methods in your viewmodels 是在构造函数中定义它们,而不是在原型中,因此它们对构造的视图模型进行操作:

    function ViewModel(data,id){
      var that = this;
      // Publics
      this.list = ko.observableArray([{name:'one'},{name:'two'},{name:'three'}]);
      this.selectedItem = ko.observable(null);
    
      this.selectItem = function(item){
        that.selectedItem(item);
        alert('Clicked on: '+that.selectedItem().name);
      };
    
    }
    

    此外,您应该使用$parent 上下文,而不是$root,因此它甚至可以在另一个视图模型中工作:

    <ul data-bind='foreach:list'>
      <li class='item' data-bind='click: $parent.selectItem'>
        <span data-bind='text:name'></span>
      </li>
    </ul>
    

    【讨论】:

    • 我明白了...直接在对象内部包含.selectItem,而不是在object.prototype 上。然后self=this 允许在.selectItem 中看到自己。将 .selectItem 从 .prototype 中移出是一个很好的解决方案,因为少数视图模型对象只会增加很少的开销。感谢您消除我的“hacky”解决方法的答案! :-)
    【解决方案2】:

    我通过这种方式获得了视图模型引用,而无需在视图模型“类”中硬编码视图模型实例。

    它涉及抓取浏览器的事件对象并使用它来获取 ViewModel 的 $rootko.contextFor(event.target).$root

    虽然这可行,但我认为必须有一种更简单的方法来消除通用 ViewModel 中的硬编码引用。

    还有其他想法吗?

    // set "selectedItem" when user clicks an <li>
    ViewModel.prototype.selectItem=function(item,event){
    
        // get a reference to the view model using ko.contextFor
        var theVM=ko.contextFor(event.target).$root;
    
        // set the "selectedItem" property on the view model
        theVM.selectedItem(this);
    
         // working!
        alert(vm1.selectedItem().name); 
    };
    

    // ViewModel Base
    var ViewModel=( function(){
    
      // ctor
      function ViewModel(data,id){
        // Publics
        this.list=ko.observableArray([{name:'one'},{name:'two'},{name:'three'}]);
        this.selectedItem=ko.observable(null);
      };
    
      // set "selectedItem" when user clicks an <li>
      ViewModel.prototype.selectItem=function(item,event){
        var theVM=ko.contextFor(event.target).$root;
        theVM.selectedItem(this);
        // It works!
        alert(vm1.selectedItem().name);
      };
    
      // Return Publics
      return(ViewModel);
    
    })();
    
    // Build a new view model
    var vm1=new ViewModel();
    
    // apply bindings
    ko.applyBindings(vm1);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
    <ul data-bind='foreach:list'>
      <li class='item' data-bind='click: $root.selectItem' >
        <span data-bind='text:name'></span>
      </li>
    </ul>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-20
      • 1970-01-01
      • 1970-01-01
      • 2013-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多