0

So I've got a section that updates based on a ko.observable called toClicked, see below:

  <div id="longInfoWindow">
     <div id="longInfo_goBack"><span class="fa fa-arrow-left"></span> Go Back</div>
     <div id="location_mainInfo">
        <h1 id="location_title" data-bind="text: toClicked.title">Title</h1>
        <h2 id="location_address" data-bind="text: toClicked.address">Address</h2>
        <h6 class="location_latlng">LAT: <span data-bind="text: toClicked.lat">LATITUDE</span></h6>
        <h6 class="location_latlng">LNG: <span data-bind="text: toClicked.lng">LONGITUDE</span></h6>
        <p id="location_description" data-bind="text: toClicked.content">
           Empty
        </p>
     </div>
  </div>

toClicked is data-bound via a for-each ul that passes in bits of information from an observableArray, so this data is constantly changing - here's what the click function looks like in the viewmodel.

var viewModel = {
  // Nav open and close via knockoutjs
  self : this,

  userQuery : ko.observable(''),
  toClicked : ko.observable({}),

  logClick : function(clicked){
    var toClicked = ko.observable({});
    toClicked().title = clicked.title;
    toClicked().lat = clicked.lat;
    toClicked().lng = clicked.lng;
    toClicked().placeID = clicked.placeID;
    toClicked().address = clicked.address;
    toClicked().content = clicked.content;
    return toClicked();
  }
};

// at the end of the document...
ko.applyBindings(viewModel);

For some reason, I can call any toClicked parameter, like toClicked.title, and I get the proper output. But I can't get anything to bind in the longInfoWindow bit of code, it removes the filler text with empty strings. Is there something I'm missing here with Knockout that's keeping it from updating appropriately?

As a side note, I've tried setting the databinds to viewModel.toClicked.title with no joy. Have also tried $root, $parent. either comes back as not defined or gives the same result.

IkeDoud
  • 77
  • 1
  • 12
  • Is `toClicked` part of your ViewModel? It seems you're assigning it to some global or local variable, it has to live on the ViewModel being bound – mfeineis Sep 06 '17 at 05:25
  • Sorry, forgot to add it. It's declared right above the logClick function, added to post. – IkeDoud Sep 06 '17 at 05:26
  • It sounds like you're moving around stuff at random and see if it works :-). You might have a look at a basic JavaScript tutorial before tackling Knockout, or even better, just ditch Knockout - it's been catatonic for quite some time and there are better options out there. I'd go with just JavaScript, but that clearly depends on your use case – mfeineis Sep 06 '17 at 05:59

2 Answers2

1

You need to change the way toClicked is accessed. Given that it is an observable, it must access the properties using syntax like toClicked().property. Another problem is that you should specify toClicked as an object, before setting properties on it.

Also, since clicked is an array, it should be accessed by index, like clicked[0].title.

Note the use of self.toClicked.valueHasMutated(); in function logClick. It tells the view model that observable variable toClicked has some non-observable properties that might have changed. As a result the view model is updated. You should avoid using it excessively.

var viewModel = function() {
  // Nav open and close via knockoutjs
  var self = this;

  self.test =  ko.observable('text');

  self.userQuery = ko.observable('');
  self.toClicked = ko.observable({});

  self.markers = ko.observableArray([
    { title: 'Eagle River Airport', lat: 45.934099, lng: -89.261834, placeID: 'ChIJdSZITVA2VE0RDqqRxn-Kjgw', content: 'This is the Eagle River Airport. Visit us for fly-ins!' }
  ]);

  self.logClick = function(clicked) {
    // var toClicked = ko.observable({});
    self.toClicked().title = clicked[0].title;
    self.toClicked().lat = clicked[0].lat;
    self.toClicked().lng = clicked[0].lng;
    self.toClicked().placeID = clicked[0].placeID;
    self.toClicked().address = clicked[0].address;
    self.toClicked().content = clicked[0].content;
    self.toClicked.valueHasMutated();
    return self.toClicked();
  };
};

// at the end of the document...
var vm = new viewModel();
ko.applyBindings(vm);
var markers = vm.markers();
vm.logClick(markers);

Your view model must also change slightly. Note the () brackets after toClicked, they are used to access the properties of an observable.

<div id="longInfoWindow">
 <div id="longInfo_goBack"><span class="fa fa-arrow-left"></span> Go Back</div>
 <div id="location_mainInfo">
    <h1 id="location_title" data-bind="text: toClicked().title">Title</h1>
    <h2 id="location_address" data-bind="text: toClicked().address">Address</h2>
    <h6 class="location_latlng">LAT: <span data-bind="text: toClicked().lat">LATITUDE</span></h6>
    <h6 class="location_latlng">LNG: <span data-bind="text: toClicked().lng">LONGITUDE</span></h6>
    <p id="location_description" data-bind="text: toClicked().content">
       Empty
    </p>
 </div>

I'd also suggest that instead of accessing toClicked directly within logClick you use something like self.toClicked to avoid ambiguity. See this.

Here's the working fiddle: https://jsfiddle.net/Nisarg0/hx0q6tt6/13/

Nisarg
  • 13,121
  • 5
  • 31
  • 48
  • Appreciate the detailed response! Just gave it a shot and unfortunately we're still in the same boat, but we can now access the properties via `toClicked()` rather than `toClicked`. We're still getting empty strings when we call via data-bind - i.e. `text: toClicked().title` is showing an empty string, but `toClicked().title` is showing the appropriate output. Thoughts? Have linted my js file and there's no errors showing up thus far, so I'm not sure if this is a hierarchy problem or something else I'm looking right past. – IkeDoud Sep 06 '17 at 05:55
  • I need to see the viewmodel working - or producing the problem. Can you create a snippet in your question? You can make it using the `<>` icon in the editor. Or if you prefer https://jsfiddle.net. – Nisarg Sep 06 '17 at 05:59
  • Struggling with getting jsfiddle to work, I'm a bit of a noob. I've updated the post to include the entire viewmodel in the meantime, working on the fiddle as it wants to keep failing on me. Appreciate your help, sorry I'm not more of a good student ;p – IkeDoud Sep 06 '17 at 06:14
  • You can modify this: https://jsfiddle.net/Nisarg0/hx0q6tt6/1/ I probably need to see how the `logClick` is called. – Nisarg Sep 06 '17 at 06:18
  • Added in part of the marker `observableArray` that I'm calling, that's typically what's getting pushed on logClick. – IkeDoud Sep 06 '17 at 06:28
  • You'll need to save it and share the updated url to me. There is an update button in the header bar. – Nisarg Sep 06 '17 at 06:30
  • Apologies, [here's the fiddle.](https://jsfiddle.net/hx0q6tt6/11/). Also if all else fails I have a [github repo](https://github.com/cipherbeta/Udacity-Neighborhood-Map/) available, don't want to take up too much of your time though, really appreciate the assistance again. – IkeDoud Sep 06 '17 at 06:33
  • Here you go with the updated fiddle: https://jsfiddle.net/Nisarg0/hx0q6tt6/13/ The problem is with the way you are accessing `clicked` object. Since it is an array, you can access `title` using `clicked[0].title`. – Nisarg Sep 06 '17 at 06:52
  • 1
    And one more thing, `self.toClicked.valueHasMutated();` tells the engine that observable `toClicked` has updated. – Nisarg Sep 06 '17 at 06:57
  • 1
    You're awesome. Thank you very much! – IkeDoud Sep 06 '17 at 07:03
0

The more obvious way without having to use valueHasMutated would be to assign directly to the observable:

  self.logClick = function(clicked) {
    self.toClicked({ 
        lat: clicked[0].lat, 
        lng: clicked[0].lng, 
        placeID: clicked[0].placeID, 
        adress: clicked[0].address,
        content: clicked[0].content
    }); 
  };

You normally do not need to use valueHasMutated when using knockout. Also there is no need to return the observable from the click handler. In your bindings you need then to access the properties as already stated like this:

<h1 id="location_title" data-bind="text: toClicked().title">Title</h1>

Knockout will automatically update the heading, whenever toClicked changed.

Mathias Mamsch
  • 321
  • 1
  • 13