简体   繁体   中英

Get object out of observable array

Why is m "undefined" in this code:

currentViewModel = ko.mapping.fromJS(viewModel);
currentViewModel.getReport = function(reportId) {
    for(var i=0;i<currentViewModel.availableReports().length;i++) {
        if(currentViewModel.availableReports()[i].id == reportId) {
            var m = currentViewModel.availableReports()[i];
            return currentViewModel.availableReports()[i];
        }
    }
}

I call getReport() as an onclick event and I want to send the report object to a view (modal) I can do a foreach on the availableReports and it's all there. When I run through the debugger, it loops through the array and finds the right one. But why can't I pull it out of the array? "m" remains undefined the the function returns undefined.

What am I missing here?

EDIT: there is a follow up question here: Can knockout.js wait to bind until an onClick?

You just need to change if(currentViewModel.availableReports()[i].id ... to if(currentViewModel.availableReports()[i].id() ... because after mapping id will become an observable, ie function.

Updated code:

currentViewModel = ko.mapping.fromJS(viewModel);
currentViewModel.getReport = function(reportId) {
    for (var i = 0; i < currentViewModel.availableReports().length; i++) {
        if (currentViewModel.availableReports()[i].id() == reportId) {
            var m = currentViewModel.availableReports()[i];
            return currentViewModel.availableReports()[i];
        }
    }
}

Demo - Fiddle .

I'll repeat the solution from @NikolayErmakov's answer here, but want to add two things to get a more complete answer. You end with:

... m remains undefined and the function returns undefined .

What am I missing here?

You're missing two things:

  1. The var m bit of the first statement inside the if is hoisted to the top of the current scope (the top of the function). This is why the debugger can tell you what m is, even if you never reach the line of code it's on.
  2. If a function invocation reaches the end of a function (as is the case for you, since you never go inside the if ) without seeing an explicit return statement, it will return undefined .

To better understand this, you should interpret your function like this:

currentViewModel.getReport = function(reportId) {
  var m;

  for (var i = 0; i < currentViewModel.availableReports().length; i++) {
    if (currentViewModel.availableReports()[i].id == reportId) {
      m = currentViewModel.availableReports()[i];
      return currentViewModel.availableReports()[i];
    }
  }

  return undefined;
}

Some people ( eg Douglas Crockford ) do recommend placing var statements at the top of a function, though it's a matter of style to some degree. I don't think many people explicitly return undefined at the end of a function, though in your case I might be explicit about that scenario and return null (or throw an Error even).

As promised, I'll repeat the actual solution, as I concur with the other answer:

  • you need to invoke id as a function to get its value (because the mapping plugin will map to observable() s.

In addition:

  • I'd retrieve the array only once
  • I'd suggest using === instead of ==

Here's my v0.5 version:

currentViewModel.getReport = function(reportId) {
    var m = null, reports = currentViewModel.availableReports();

    for (var i = 0; i < reports.length; i++) {
        if (reports[i].id() === reportId) {
            m = reports[i];
            return m;
        }
    }

    return m;
}

But I'd optimize it to this v1.0:

currentViewModel.getReport = function(reportId) {
    var reports = currentViewModel.availableReports();

    for (var i = 0; i < reports.length; i++) {
        if (reports[i].id() === reportId) {
            return reports[i];
        }
    }

    return null;
}

For completeness, here's another version that utilizes filter on arrays:

currentViewModel.getReport = function(reportId) {
  var reports = currentViewModel.availableReports().filter(function(r) { return r.id() === reportId; });
  return reports.length >= 1 ? reports[0] : null;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM