Help with observableArray not updating in view

1,050 views
Skip to first unread message

Sean Malone

unread,
Feb 26, 2013, 3:04:38 AM2/26/13
to duran...@googlegroups.com

Hello, 

I am new to Durandal. I have a little experience with KnockoutJS. I am trying to populate an observableArray from json retrieved from a php file. I am then trying to load the array into a table. There seems to be an issue when loading the data into the table upon first load of the page. I perform a system.log(customers) just to make sure the data is in the observableArray, which it is. When I refresh the page, the data is loaded into the table like I want it to. It appears as if the observableArray is getting populated, but the view is not updating.

I was wondering what I am doing wrong for it to not load on the first load of the page. Here is the test site I've been playing with durandal: http://dev.codeplanetapps.com/spa/. Once it loads, click on "Customers." The page in question is http://dev.codeplanetapps.com/spa/#/customers. When that page is loaded directly, it works fine, but when I load the app from the main page and then switch to customers, it doesn't load the table correctly. Thanks in advance for any help.

Here is the view:

<div>
<!-- Form to add new customer -->
<form class="form-inline customerForm">
    <span>Add New Customer</span>
    <input type="text" data-bind="value: inputName" placeholder="Name" />
    <input type="text" class="date input-small" data-bind="value: inputDOB" placeholder="Date of Birth" />
    <input type="text" class="input-small" data-bind="value: inputPhone" placeholder="Phone" />
    <input type="text" data-bind="value: inputEmail" placeholder="Email" />
    <button type="submit" class="btn btn-primary" data-bind="click: submitCustomer">Add</button>
</form>

<table class="table table-hover table-bordered customerTable">
    <thead>
        <tr>
            <th>Name</th>
            <th>DOB</th>
            <th>Phone Number</th>
            <th>Email</th>
            <th></th>
        </tr>
    </thead>
    <tbody data-bind="foreach: customers">
        <tr>
            <td data-bind="text: name"></td>
            <td data-bind="text: dob"></td>
            <td data-bind="text: phone"></td>
            <td data-bind="text: email"></td>
            <td><button class="btn btn-danger btn-mini" data-bind="click: $root.removeCustomer">Delete</button></td>
        </tr>
    </tbody>
</table>

And here is the viewmodel:

define(function(require){ 
// Load System for debugging
var system = require('durandal/system');

// Customer Structure
function Customer(data) {
    this.name  = ko.observable(data.name);
    this.dob   = ko.observable(data.dob.substring(5,7) + '/' 
        + data.dob.substring(8,10) + '/' + data.dob.substring(0,4));
    this.phone = ko.observable(data.phone);
    this.email = ko.observable(data.email);
};

// Form observables
var inputName  = ko.observable('');
var inputDOB   = ko.observable('');
var inputPhone = ko.observable('');
var inputEmail = ko.observable('');
// Customers array
var customers = ko.observableArray([]);

return {
    inputName: inputName,
    inputDOB: inputDOB,
    inputPhone: inputPhone,
    inputEmail: inputEmail,
    customers: customers,
    // This performs any needed functionality after the page loads
    activate: function(data) {
        // Change the selected nav item 
        $('.customerNav').addClass('active');
        $('.firstNav').removeClass('active');
        $('.secondNav').removeClass('active');

        // Get current customers from database and add to customers observableArray
        $.getJSON(
            // Backend script
            'php/query.php', 
            // Variables sent to query.php
            {
                mode: 'select', 
                table: 'customers', 
                'fields[]': '*', 
                'values[]': '*'
            }, 
            // Callback function
            function(data) {
                var customer = $.map(data, function(item) {return new Customer(item) });
                customers(customer);
            }
        );

        // For some reason I couldn't get the datepicker to work without make the page 
        // wait 50 milliseconds.
        setTimeout(function() {
        $('.date').datepicker();
        },500);
    }
};

});

Also, as a side note, the jQuery at the top and bottom of the activate function only work when I surround them in a setTimeout() function. I left the datepicker as an example. It's not as crucial, but if anyone knows how to fix that, too, I would greatly appreciate it.


Thanks,

Sean

Message has been deleted

Sean Malone

unread,
Feb 26, 2013, 3:30:47 AM2/26/13
to duran...@googlegroups.com
I actually found an old post that talked about the viewAttached method. I moved all of my jQuery into there, and it fixed those issues. I also don't seem to be getting observableArray issue anymore.

Would having my jQuery calls inside the activate function screw up any of the Knockout stuff? I don't see how it would.

I'm not quite satisfied with my findings. I would still really like more opinions on this. 

Thanks!

Rob Eisenberg

unread,
Feb 26, 2013, 9:27:35 AM2/26/13
to Sean Malone, duran...@googlegroups.com
The reason that your code gets messed up inside activate is because it is asynchronous, but you aren't telling Durandal that it is. Activate is the correct place to put this type of data loading code, but you must return the promise from the ajax call back to Durandal, so it knows to halt databinding until after the call is complete. Otherwise, it's possible for ko.applyBindings to run *while* your data is populating which will mess up ko's observability. All jQuery ajax calls returns a promise. So, just return that from the activate method and everything should work. Your dataPicker call is a different problem. That needs to live in the viewAttached function because, when active is called, there's no view available yet. Your set timeout is a hack which may or may not work depending on how fast the browser is and when it triggers that. Move that to the viewAttached function...or better yet, create a custom ko binding handler and apply it with a data-bind attribute. That's the best way to do this sort of thing.


--
You received this message because you are subscribed to the Google Groups "DurandalJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to durandaljs+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Rob Eisenberg,
Blue Spire Consulting, Inc.
Caliburn Project

Sean Malone

unread,
Feb 26, 2013, 10:42:30 AM2/26/13
to duran...@googlegroups.com

Hey Rob,

Thanks for the quick reply. Your answer helped a lot. I guess I missed the part about returning the promise. I have one more quick question. You mentioned that your return the ajax call to halt databinding until it's complete. If I were to have multiple ajax calls (for example sake), would I only need to worry about returning the last one?

Also, just a quick praise. I think you're doing amazing work. I've been a php developer for many years. I finally decided to checkout what all fun the frontend guys are having. I decided to go with jQuery, Knockout, Require, Sammy and Bootstrap. In doing my research, I found Durandal. It's awesome that you packaged exactly the libraries I needed into one framework.

I apologize in advance for any silly questions I might have in the near future, ha ha.

Thanks,

Sean

Rob Eisenberg

unread,
Feb 26, 2013, 10:43:23 AM2/26/13
to Sean Malone, duran...@googlegroups.com
Your questions aren't silly, trust me. Regarding multiple ajax calls, you can chain promises together and return the result of the chain. If you google jQuery ajax promises I bet you will find some examples.


--
You received this message because you are subscribed to the Google Groups "DurandalJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to durandaljs+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 
Reply all
Reply to author
Forward
0 new messages