1

So I've successfully been able to implement autocomplete which adds item to a list (fiddle).

Now I need to use jQuery UI autocomplete to populate some data fields. Still trying to wrap my head around Knockout, I don't think I've successfully bound the autocomplete.

I thought data-bind="autocompleteAddress: items would trigger my data-bind="autocompleteAddress, but nothing is happening (see me fiddle here)

Is there something obvious I'm missing?

My code looks like this:

// HTML (the 'source:' is used for my ajax call. Not used in the fiddle)
    <input  
        type="text" 
        data-bind="autocompleteAddress: {source: '/address/autocompleteAddress'}" 
        name="Address[street1]" 
        value="Stovner Senter 3" 
        placeholder="Enter street name" 
        class="street1 form-control ui-autocomplete-input" 
        autocomplete="off" />


// JS    
var search_data = [
 {"id": "7186","street1": "Bose","street2": "","postal_code": "0521","city": "Oslo","country":"Norway"}, 
 {"id": "1069","street1": "BOSS Black","street2": "","postal_code": "0531","city": "Oslo","country":"Norway"}, 
 {"id": "1070","street1": "BOSS Green","street2": "","postal_code": "0522","city": "Oslo","country":"Norway"}
];

ko.bindingHandlers.autocompleteAddress = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {        
        var postUrl = site_url + allBindingsAccessor().source; //Not used here
        var self = viewModel;        

        $(element).autocomplete({
            minLength: 2,
            source: function (request, response) {
                response(search_data);
            },
            select: function (e, ui) {
                var item = ui.item;

            }
        }).data("uiAutocomplete")._renderItem = function (ul, item) {
            return jQuery("<li></li>")
                .data("ui-autocomplete", item)
                .append("<a>" + item.street1 + ' ' + item.city + "</a>")
                .appendTo(ul);
        }

    }
};
Community
  • 1
  • 1
Steven
  • 19,224
  • 47
  • 152
  • 257
  • I had worked on this from your older/deleted question. See [fiddle](http://jsfiddle.net/origineil/P8N77/15/). I updated the data to that provided in this post. – Origineil Jun 01 '14 at 15:17
  • That's fantastic @Origineil! Can you post a short answer here? – Steven Jun 01 '14 at 15:23

2 Answers2

2

So based on the original question (which was deleted), the problem was that you wanted to bind a set of address properties when a user selected an item from autocomplete.

The same problem as mentioned by GSerjo was present in that fiddle as well -- no ko.applyBindings was used. It was there just commented out, so that fixed the autocomplete showing up.

The next issue was that the values weren't showing up in the fields. Adding return false at the end of the select function of the autocomplete seemed to address that issue. I don't know what significance that has, I'll have to look into that.

Other than that, I cleaned up some of the html and provided an Address constructor.

var Address = function (data) {
 this.address_id = ko.observable(data.id);
 this.street1 = ko.observable(data.street1);
 this.street2 = ko.observable(data.street2);  
 this.postal_code = ko.observable(data.postal_code);
 this.city = ko.observable(data.city);
 this.country = ko.observable(data.country);  
};

The viewmodel:

var vm = {
  items: ko.observableArray(search_data),
  address: ko.observable({})
}

The autocomplete binding:

<input type="text" class='street1 form-control' data-bind="autocompleteAddress: items, valueUpdate:'afterKeyDown', value: address().street1, attr:{placeholder: 'Enter street name'}"></input>

When a user makes a selection,

 select: function (e, ui) {
          viewModel.address(new Address(ui.item))
          return false;
         }

See the updated fiddle

Origineil
  • 3,108
  • 2
  • 12
  • 17
  • There is one problem with this solution. You are resetting the input fields. I need keep the content. How do you go about that? I feel this is a "global" issue with Knockout. You have to write extra code in order to keep server side loaded data. Is there a way to keep server side data? – Steven Jun 01 '14 at 15:59
  • Not exactly clear on what you mean by "keep" and "resetting". If you set the initial `viewmodel` to be that of the server-side data, then that is the data that will be displayed. So instead of `address: ko.observable({})` you would replace the empty object `{}` with some `address` item from the server. Either `search_data` or `items` would always be the representation of `loaded data` from the server. – Origineil Jun 01 '14 at 18:10
  • So I have to pass the server data to the viewModel then in turn put it back in the HTML? That seems a bit backward. Can't I just output the data in the html `= $some->data; ?>` and tell Knockout to leave it alone? :) I know Angular JS leaves the data as is when it comes from the server. – Steven Jun 01 '14 at 19:08
  • 1
    The viewModel gets put into the HTML via `ko.applyBindings` you just have to tell it how you want it displayed. The HTML is `bound` to the model, so if you have "loaded" something outside of the `apply`, knockout doesn't know about it. So if I'm not mistaken, you are asking why you can't say something like `value='Oslo'` in the html and have knockout not change that? – Origineil Jun 01 '14 at 19:34
  • Yeah. But not just `value='Oslo'`. Also `
    Some data from server here
    `. Once I add `data-bind`, the content of the div / input gets empty. When the page renders, it also renders the content retrieved from server (right), so Knockout should know it has content and therefore should return that content.
    – Steven Jun 01 '14 at 20:22
  • 1
    Discussion moved to separate [question](http://stackoverflow.com/questions/23984658/how-do-i-keep-data-loaded-from-the-server-from-getting-blanked-out-by-knockout) – Origineil Jun 02 '14 at 16:14
1

Knockout doesn't apply your binding because ko.applyBindings is absent. So, just add ko.applyBindings({}); at the bottom of the code.

Here's updated version

Note: you can debug the code with F12 and debugger; key word. For instance:

  • Write debugger; in the init function
  • Press F12 in your favorite browser and you see that the binding was not called
GSerjo
  • 4,725
  • 1
  • 36
  • 55
  • Yeah, I was wondering about that because I'm already using `ko.applyBindings`for two other view models. So I thought that would be enough. Can I somehow do this `var addViewModel = ko.bindingHandlers.autocompleteAddress...` ? I'm using Firebug all the time, but didn't know about the `debuger;`. Thanks. – Steven Jun 01 '14 at 13:36
  • yes, you can, it's JavaScript :) but what is your aim? You can move `ko.bindingHandlers.autocompleteAddress` script on top loading level. i.e. first of all your binding will be initialized and then applied – GSerjo Jun 01 '14 at 14:23