1

I just started developing a Map application using knockout.js. I somewhat know how the binding context works but I don't understand how the showCoord function is accessible without $parent. Here is the code

var MapApplication = function() {
 // member function
 self = this;

 var map;

 self.locations = ko.observableArray([
  {name: 'Museum', coord: {lat: -37.669012, lng: 144.841027}},
  {name: 'Ariport', coord: {lat: -37.669012, lng: 144.841027}}
 ]);

 self.showCoord = function(obj) {
  console.log(obj.coord);
 }

 var init = function() {
  ko.applyBindings(MapApplication);
 };

 $(init);
}();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="places">
  <ul data-bind="foreach: locations">
 <li data-bind="click: showCoord, text: name"></li>
  </ul>
</div>
Ray
  • 3,864
  • 7
  • 24
  • 36

1 Answers1

0

Try using $parent and see what happens.

In fact, try changing ko.applyBindings(MapApplication) to ko.applyBindings(). It will still work the same way.

What's happening is that locations is behaving like a child function of Window, and it's own parent MapApplication is unrecognized. This is because you haven't initialized MapApplication, and you cannot initialize it before it is created.

Bonus - try modifying your code like this:

self.showCoord = function(obj) {
   console.log(obj.coord);
   console.log(self);
}

and check the value of 'self'.


The correct way to bind would be this:

var MapApplication = function() {
    // member function
    self = this;

    var map;

    self.locations = ko.observableArray([
         {name: 'Museum', coord: {lat: -37.669012, lng: 144.841027}},
         {name: 'Ariport', coord: {lat: -37.669012, lng: 144.841027}}
    ]);

    self.showCoord = function(obj) {
         console.log(obj.coord);
    }                
};
var init = function() {
    ko.applyBindings(new MapApplication());
};
$(init);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="places">
    <ul data-bind="foreach: locations">
        <li data-bind="click: $parent.showCoord, text: name"></li>
    </ul>
</div>

Update If you want to use IIFE, the function should not try to bind itself - it needs to bind one of its objects. So you need to add an encapsulating function and make MapApplication an object. See this. This also allows you to bind multiple objects to different parts of the html, if needed. Here's how it might look:

var applyBinding = function(){
    var MapApplication =  {
        locations : ko.observableArray([
            {name: 'Museum', coord: {lat: -37.669012, lng: 144.841027}},
            {name: 'Ariport', coord: {lat: -37.669012, lng: 144.841027}}
        ]),
        showCoord :function (obj) {
            console.log(obj.coord);
        }
    };
    ko.applyBindings(MapApplication);
}();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
 <div class="places">
     <ul data-bind="foreach: locations">
         <li data-bind="click: $parent.showCoord, text: name"></li>
     </ul>
 </div>
Ray
  • 3,864
  • 7
  • 24
  • 36
  • Thanks for the answer Ray. I know the correct way would be the one you suggested but I was trying to organize the code using the module pattern and IIFE. – Amol Borgaonkar Nov 06 '17 at 05:28