Question

I am dynamically building a TableView in my controller, which works fine: The initial table displays the initial collections data as expected.

The problem is that the TableView's 'click' event listener is ignored when I click on the table rows. I am testing in the browser, and I never even see the console event file (see comments in controller file). All relevant code snippets below:

In my alloy.js I setup a backbone collection:

function defaultTodo(name) { return {name: name, done: false}; }
function doneTodo(name) { return {name: name, done: true}; }
Alloy.Collections.todos = new Backbone.Collection();
Alloy.Collections.todos.reset([
        defaultTodo('Apples'), // create not yet done todo
        defaultTodo('Banana'),
        defaultTodo('Paper Towels'),
        defaultTodo('Broccoli'),
        doneTodo('Beans'),    // create already done todo
        doneTodo('Water'),
        doneTodo('Blueberries'),
        doneTodo('Stir Fry')
])

Here is my index.js controller:

var todos = Alloy.Collections.todos;

function redrawTable() {

    // clear all the old data
    // See http://developer.appcelerator.com/question/49241/delete-all-rows-in-a-tableview-with-a-single-click
    $.table.setData([]);

    // Create and add the TableViewSections
    var alreadyDone = Ti.UI.createTableViewSection({ headerTitle: "Already Done" });
    var needsDoing = Ti.UI.createTableViewSection({ headerTitle: "Needs Doing" });
    $.table.appendSection(needsDoing);
    $.table.appendSection(alreadyDone);

    // Add the todo to the appropriate sections
    todos.forEach(function(todo) {
        var section = todo.get('done') ? alreadyDone : needsDoing;
        addEntry(todo, section);
    });

    // Add the click listener
    // THIS LISTENER IS IGNORED ********************************
    $.table.addEventListener('click', function(e) {
        console.log(e);
        todos.at(e.index).set('done', true);
        todos.trigger('change');
    }); 

    // Helper function to add a row to a section
    function addEntry(todo, section) {
        var row = Ti.UI.createTableViewRow({
            title: todo.get('name'),
            className: "row"
        });
        section.add(row);
    }
}

// Redraw our table each time our todos collection changes
todos.on('change', redrawTable);

// Trigger a change event to draw the initial table
todos.trigger('change');

$.index.open();

And here is index.xml view file:

<Alloy>
    <Window class="container">
        <Label id="test" class="header">My Grocery List</Label>
        <TextField id="newItem"/>
        <TableView id="table">
        </TableView>
    </Window>
</Alloy>

UPDATE: Working Code

In addition to the changes below, I also added onClick="markDone" to the xml.

function markDone(e) {
    console.log(e.row.todo);
    e.row.todo.set('done', true);
    todos.trigger('change');
} 

function redrawTable() {

    // clear all the old data
    // See http://developer.appcelerator.com/question/49241/delete-all-rows-in-a-tableview-with-a-single-click
    $.table.setData([]);
    var rows = []; 
    var done = [];
    var doing = [];

    // Add the todo to the appropriate sections
    todos.forEach(function(todo) {
        var row = Ti.UI.createTableViewRow({
            title: todo.get('name'),
            className: "row"
        });
        row.todo = todo; 

        todo.get('done') ? done.push(row) : doing.push(row);

    });

    // Create and add the TableViewSections
    rows.push(Ti.UI.createTableViewSection({ headerTitle: "Needs Doing" }));
    rows = rows.concat(doing);  
    rows.push(Ti.UI.createTableViewSection({ headerTitle: "Already Done" }));
    rows = rows.concat(done);

    $.table.setData(rows);

};
Was it helpful?

Solution

I created brand new project using files which you provided and eventListener is working perfectly fine. However there are couple other bugs:

  1. Creating listener inside redrawTable() function, which is executed every time you click on something in TableView. As a result at the beginning you have one eventListener but after every click all listeners are duplicated.

  2. Using index property in event handler to find index of Backbone model object to update. index property is indicating at which place given row was displayed in your TableView. When you are moving rows between sections their index are changing. In your case it's better to check e.row.name property and use Backbone.Collection.findWhere() method. If user can have two items with the same name on the list, then you have to create additional property to determine which object in model should be changed.

  3. You should add rows to section before section are added to table. In your case table is very simple so instead of doing loops you can just create one simple array of objects (with title and optional header properties) and pass it to $.table.setData().

  4. It's good to wait for postlayout event triggered on main view before triggering any custom events to make sure that the whole view was created and all objects are initiated.

Check rewrote index.js code below to see how it could be done.

var todos = Alloy.Collections.todos;

function redrawTable() {
    var done = todos.where({done: true}).map(function(elem) {
        return { title: elem.get('name') };
    });
    if (done.length > 0) {
        done[0].header = 'Already Done';
    }

    var notdone = todos.where({done: false}).map(function(elem) {
        return { title: elem.get('name') };
    });
    if (notdone.length > 0) {
        notdone[0].header = 'Needs Doing';
    }

    $.table.setData( done.concat(notdone) );
}

$.table.addEventListener('click', function(e) {
    todos.where({ name: e.row.title }).forEach(function(elem){
        elem.set('done', !elem.get('done'));
    });
    todos.trigger('change'); // Redraw table
});

// Redraw our table each time our todos collection changes
todos.on('change', redrawTable);

// Trigger a change event to draw the initial table
$.index.addEventListener('postlayout', function init(){
    todos.trigger('change');
    // Remove eventListener, we don't need it any more.
    $.index.removeEventListener('postlayout', init);
})

$.index.open();

redrawTable() could have a little more refactor but I left it so it's easier to read.

To read more about manipulating TableView check this Appcelerator documentation page.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top