views:

105

answers:

1

I'm trying to create a dummy application that maintains a list of tasks.

For now, all I'm trying to do is add to the list. I enter a task name in a text box, click on the add task button, and expect the list to be updated with the new item and the task name input to be cleared. I only want to be able to add tasks if the task name is not empty. The below code is my implementation, but I have a question regarding the binding.

I'm binding the textbox's text variable to a string in my view model, and the button's disable variable to a boolean in my view model.

I have a trigger to update the disabled state when the task name changes. When the binding of the task name happens the boolean is updated accordingly, but the button still appears disabled. But then when I mouse over the button, it becomes enabled. I believe this is due to JavaFX 1.3's binding being lazy - only updates the bound variable when it is read.

Also, when I've added the task, I clear the task name in the model, but the textbox's text doesn't change - even though I'm using bind with inverse.

Is there a way to make the textbox's text and the button's disabled state update automatically via the binding as I was expecting?

Thanks,

James

AddTaskViewModel.fx:

package jamiebarrow;

import java.lang.System;

public class AddTaskViewModel {

function logChange(prop:String,oldValue,newValue):Void {
    println("{System.currentTimeMillis()} : {prop} [{oldValue}] to [{newValue}] ");
}


    public var newTaskName: String on replace old {
        logChange("newTaskName",old,newTaskName);
        isAddTaskDisabled = (newTaskName == null or newTaskName.trim().length() == 0);
    };
    public var isAddTaskDisabled: Boolean on replace old {
        logChange("isAddTaskDisabled",old,isAddTaskDisabled);
    };
    public var taskItems = [] on replace old {
        logChange("taskItems",old,taskItems);
    };

    public function addTask() {
        insert newTaskName into taskItems;
        newTaskName = "";
    }

}

Main.fx:

package jamiebarrow;

import javafx.scene.control.Button;
import javafx.scene.control.TextBox;
import javafx.scene.control.ListView;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.scene.layout.HBox;

def viewModel = AddTaskViewModel{};

var txtName: TextBox = TextBox {
            text: bind viewModel.newTaskName with inverse
            onKeyTyped: onKeyTyped
        };

function onKeyTyped(event): Void {
    txtName.commit(); // ensures model is updated
    cmdAddTask.disable = viewModel.isAddTaskDisabled;// the binding only occurs lazily, so this is needed
}

var cmdAddTask = Button {
            text: "Add"
            disable: bind viewModel.isAddTaskDisabled with inverse
            action: onAddTask
        };

function onAddTask(): Void {
    viewModel.addTask();
}

var lstTasks = ListView {
            items: bind viewModel.taskItems with inverse
        };

Stage {
    scene: Scene {
        content: [
            VBox {
                content: [
                    HBox {
                        content: [
                            txtName,
                            cmdAddTask
                        ]
                    },
                    lstTasks
                ]
            }
        ]
    }
}
+1  A: 

For the record, this question have been answered in question regarding binding button’s disabled state thread.

Short summary: there is a bug in JavaFX 1.3 for bind with inverse to fields in objects. There is a workaround, but it breaks encapsulation.

PhiLho
Thanks again PhiLho. Haven't actually implemented the change but looks good, so trust it works for now :) Will check again when I get a chance.
jamiebarrow