Knockout JS: indexOf always returns -1?

青春壹個敷衍的年華 提交于 2019-12-11 02:50:24

问题


Background

I'm trying to build a gradebook app, mostly as a learning exercise. Currently, I have two models, a student, and an assignment. I decided to store all score-related information inside the student rather than with each assignment. Perhaps there's a better approach.

Regardless, I already have the average score for each student, i.e., her grade in the class. I'm now at the point where I want to calculate the average score on each assignment. This is where I run into trouble, as it's slightly trickier. I'm currently using the following method:

JS Bin (entire project): http://jsbin.com/fehoq/84/edit

JS

  var _this = this;
  ...

  // get index i of current assignment; 
  // then, for each student, grab her grade for assignment i;
  // add each grade at i, then divide by # of students;
  // return this value (the mean);

  this.workMean = ko.computed(function (work) {
        var i = parseFloat(_this.assignments.indexOf(work)); 
        var m = 0;
        var count = 0;
        ko.utils.arrayForEach(_this.students(), function (student) {
            if (!isNaN(parseFloat(student.scores()[i]))) {
                m += parseFloat(student.scores()[i]);
            } else {
                count += 1;
            }
        });
        m = m / (_this.students().length - count); 
        return m.toFixed(2);   
    });

And I'm binding this to the HTML in the following way:

HTML

<tbody>
  <!-- ko foreach: students -->
    <tr>
        <td><input data-bind="value: fullName + ' ' + ($index()+1)"/></td>  
        <!-- ko foreach: scores -->  
        <td><input data-bind="value: $rawData"/></td>
        <!-- /ko --> 
        <td data-bind="text: mean" />
        <td><input type="button" value="remove" data-bind="click: $root.removeStudent.bind($root)". /></td>
    </tr> 
  <!-- /ko -->
    <tr>
      <td>Class Work Average</td> 
      <!-- ko foreach: assignments -->
      <td data-bind="text: $root.workMean"></td>
      <!-- /ko -->
    </tr>  
</tbody>    

The problem is that something I'm doing here - and I think it's the workMean() method - is completely breaking my app. While trying to debug, I noticed that if I simply comment out the entire method save i, then return i and bind it to the lower foreach: assignments, it consistently returns -1 (for each assignment).

The Knockout docs tell me that means there's no match when I call indexOf, but I'm lost as to why. Guidance appreciated.


回答1:


Besides the issue that DCoder identified — observables not taking parameters -, you had another bug here:

score = parseFloat(student.scores()[i]);

should have been

score = parseFloat(student.scores()[i]());

The nth (or i-th) element of the observable array you access there is itself an observable, so before, you where passing a function to parseFloat, which always yields NaN.

Here is a working version: http://jsbin.com/lejezuhe/3/edit

By the way: after DCoders changes,

<td data-bind="text: $root.workMean($data, $index())"></td>

binds against a normal function, not an observable. So why does this still work?

RP Niemeyer, one of the Knockout core members writes:

In Knockout, bindings are implemented internally using dependentObservables, so you can actually use a plain function in place of a dependentObservable in your bindings. The binding will run your function inside of a dependentObservable, so any observables that have their value accessed will create a dependency (your binding will fire again when it changes).

(computed observables used to be called "dependentObservables" in earlier versions of Knockout)

For these type of issues, it really helps to be familiar with a debugger such as the one in the Chrome Developer Tools. Being able to step through your code line by line and seeing what arguments and variables actually contain is immensely helpful.

The Chrome Knockout context debugger is worthwhile to have when debugging bindings, because you can click on any DOM element and see the binding context:

Lastly, using ko.dataFor() in the console allows you to poke around any of your existing Knockout models and viewmodels bound to the DOM:

In the Chrome console, $0 is always a reference to the DOM element you have currently selected in the Elements panel - here, a <td>.



来源:https://stackoverflow.com/questions/23299194/knockout-js-indexof-always-returns-1

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!