Pete Herc
Pete Herc

Reputation: 163

Binding associative arrays in Knockout.Js from ajax JSON response

I'm having issues binding a json response with knockout.js. Can anybody help point me in the right direction. There are two versions of code below. Version 1 is working but users need to search. If they search more than once it breaks as the bindings have already been applied.

Version 1

var self = this;

self.Jobs = ko.observableArray();
self.Count = ko.observable();
self.Score = ko.observable();
self.Highlights = ko.observable();
self.Document = ko.observableArray();
self.id = ko.observable();
self.title = ko.observable();
self.description = ko.observable();
self.company = ko.observable();

self.error = ko.observable();


var jobsUri = '/home/search/';


function getAllJobs() {

    var search = $('#search').val();

    $.post(jobsUri, {
        search: search
    },

    function (data) {
        viewModel = {
        Jobs: data.Results,
        Count: data.Count,
        Document: Jobs.Document,
        id: Document.id,
        title: Document.title,
        description: Document.description,
        company: Document.company
        }
        ko.applyBindings(viewModel, $('#jobs').get(0));
    });

}

Version 2 appears to work and the elements appear in the DOM but for some reason I don't seem to be able to access the elements with knockout to display on frontend.

Version 2

function ViewModel() {

    var self = this;

    self.data = ko.observableArray();

    getAllJobs();

}


function getAllJobs() {

    var self = this;

    self.Jobs = ko.observableArray([]);
    self.Count = ko.observable();
    self.Score = ko.observable();
    self.Highlights = ko.observable();
    self.Document = ko.observableArray();
    self.id = ko.observable();
    self.title = ko.observable();
    self.description = ko.observable();
    self.company = ko.observable();
    self.error = ko.observable();


    var jobsUri = '/home/search/';

    var search = $('#search').val();

    $.post(jobsUri, {
        search: search
    },

    function (data) {
        viewModel = {
            Jobs: data.Results,
            Count: data.Count,
            Document: Jobs.Document,
            id: Document.id,
            title: Document.title,
            description: Document.description,
            company: Document.company,

        }
    });

}


var viewModel = new ViewModel();
ko.applyBindings(viewModel);

The html is very basic. As stated, it works with the version 1 code above, but not with version 2!!

<div class="row" id="jobs search">

    <input type="text" id="search" class="form-control" value="" placeholder='"Senior Manager" "Corporate Finance" London "Investment Banking"' />

     <input type="submit" class="typeahead search-submit btn btn-success btn-block " value="Search" onclick="getAllJobs();" />

    <hr />

    <div data-bind="foreach: Jobs">
        <div class="jobs-list">
            <h3 class="job-title" data-bind="text: Document.title"></h3>
            <p data-bind="text: Document.company"></p>
            <p data-bind="text: Document.description"></p>

        </div>
    </div>
</div>

I feel like I've tried everything possible, but just not cracking it!! Any help greatly appreciated. Thanks in advance

Upvotes: 0

Views: 449

Answers (1)

Tomalak
Tomalak

Reputation: 338248

Your code violates the fundamental principle of knockout applications: viewmodel/view separation.

You are not supposed to have any DOM interactions or jQuery code inside your viewmodel. (Except for jQuery Ajax calls, but you could easily substitute them for another Ajax library. In fact, unless you are using jQuery UI or other tools that have an explicit jQuery dependency, consider dropping jQuery entirely in a knockout application. All DOM manipulation should be done through knockout anyway and just for the sake of having convenient Ajax jQuery is a little bit too heavy.)

If you want to access a value (like a search term) inside your viewmodel, make an observable for it and bind that observable to a control via the value binding.

Like this:

function JobSearchForm() {
    var self = this;

    self.searchTerm = ko.observable();
    self.jobs = ko.observableArray();

    self.doSearch = function () {
        // todo: empty search term handling
        $.get('/home/search/', {
            search: self.searchTerm()
        }).done(function (data) {
            self.jobs(data.Results);
        });
    };
}

var viewModel = new JobSearchForm();
ko.applyBindings(viewModel);

Corresponding view:

<div class="row" id="jobs search">
    <input type="text" data-bind="value: searchTerm" class="form-control" placeholder='...' />
    <button data-bind="click: doSearch" class="typeahead search-submit btn btn-success btn-block">Search</button>
    <div data-bind="foreach: jobs">
        <div class="jobs-list" data-bind="with: Document">
            <h3 class="job-title" data-bind="text: title"></h3>
            <p data-bind="text: company"></p>
            <p data-bind="text: description"></p>
        </div>
    </div>
</div>

Bindings are the only places where viewmodel and view are supposed to interact.

Since I have no idea what your server returns I can only speculate about the foreach binding. When in doubt you can always drop in

<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>

to see a visual representation of your data in each iteration. Or you try the Knockoutjs context debugger Chrome extension for more advanced debugging.

Upvotes: 1

Related Questions