Knockout.js Setting SelectedOption to an object

2019-09-02 09:42发布

I'm on my first Knockout project and working trying to figure out the best way to handle this situation.

Hopefully this dumbed down example serves it's purpose.

I am trying to edit a 'project' object which has a property for 'employee'.

function Project(data) {
    var self = this;
    self.Name = data.Name;
    self.Employee = data.Employee;
}

function Employee(data) {
    var self = this;
    self.ID = data.ID;
    self.Name = data.Name;
}

I first load up my project and then set the 'selectedEmployee'.

var me = new Employee ({ ID: 1, Name: 'Aaron'});

function vm() {
    var self = this;

    self.Project = ko.observable(new Project({ Name:'Knockout', Employee:me}));

    self.selectedEmployee = ko.observable(self.Project().Employee);

    self.employees = ko.observableArray([
                                        new Employee ({ID: 1,Name: 'Aaron'}),
                                        new Employee ({ID: 2,Name: 'George'}),
                                        new Employee ({ID: 3,Name: 'Ross'})
                                        ]);
}

ko.applyBindings(vm);

I have a drop down list which holds a list of employees:

<select data-bind="options: employees, optionsText: 'Name',value: selectedEmployee, optionsCaption:'Pick Employee'"></select>

My Problem: The drop down list does not have an employee selected. I'm guessing this is because the 'selectedEmployee' object and the Project.Employee object are not the same.

This example works:

var me = new Employee ({ ID: 1, Name: 'Aaron'});

function vm() {
    var self = this;

    self.Project = ko.observable(new Project({ Name:'Knockout', Employee:me}));

    self.selectedEmployee = ko.observable(self.Project().Employee);

    self.employees = ko.observableArray([
                                        me,
                                        new Employee ({ID: 2,Name: 'George'}),
                                        new Employee ({ID: 3,Name: 'Ross'})
                                        ]);
}

ko.applyBindings(vm);

So, what is the best practice of making the 'Employee' on the project match what is in the drop down list? Do I need to retrieve the Employee object from the list of 'employees' (based on ID) when I load it up?

I tried using an observable property for the EmployeeName (to use when displaying the value [read-only]) and EmployeeID (to use when selecting from the list), but the EmployeeName is not updated.

Seems like there should be an easy way to do this, but like I said, this is my first Knockout project.

Here's my fiddle

Thank you in advance.

标签: knockout.js
1条回答
别忘想泡老子
2楼-- · 2019-09-02 10:16

Okay I'm going to provide the answer I came up with based on the comment by @nemesv.

Here's my HTML. Notice I am setting the options to an array of objects. The optionsText and optionsValue properties are set as you might expect. My custom binding selectById is set to the selected object in my View Model.

<select data-bind="options: employees, 
            optionsText: 'Name',
            optionsValue: 'ID',                    
            selectById: selectedEmployee, 
            optionsCaption:'Pick Employee'">
</select>
<br><br>
Selected Employee:
<span data-bind="text: ko.toJSON(selectedEmployee)"></span>

Here's my view model:

function Project(data) {
    var self = this;
    self.Name = data.Name;
    self.Employee = data.Employee;
}

function Employee(data) {
    var self = this;
    self.ID = data.ID;
    self.Name = data.Name;
}

ko.bindingHandlers.selectById = {
    init: function (element, valueAccessor, allBindings) {    
        $(element).change(function() {
            var emps = ko.unwrap(allBindings.get('options'));
            var newSelectedValue = $(element).find("option:selected").val();
            var va = valueAccessor();
            if (!!newSelectedValue) {
                va(ko.utils.arrayFirst(emps, function(item) { return item.ID == newSelectedValue }));                                
            } 
            else va(null);                                
        });     
    },
    update: function (element, valueAccessor) {
        var selectedItem = ko.unwrap(valueAccessor());   
        $(element).val(selectedItem ? selectedItem.ID : '');        
    }
}

var me = new Employee({ ID: 1, Name: 'Aaron' });

function output(sender) {
    //console.warn($(arg.target));
    console.info('New Selected Value: ' + $(sender.target).find("option:selected").val());
}

function vm() {
    var self = this;

    self.Project = ko.observable(new Project({ Name: 'Knockout', Employee: me }));

    self.selectedEmployee = ko.observable(me);

    self.employees = ko.observableArray([
                                        new Employee({ ID: 1, Name: 'Aaron' }),
                                        new Employee({ ID: 2, Name: 'George' }),
                                        new Employee({ ID: 3, Name: 'Ross' })
                                        ]);
}

ko.applyBindings(vm);

I have no doubt that this solution could be improved upon, feel free to pass on better solutions/tips. Frankly I think this kind of option should be baked into Knockout.

查看更多
登录 后发表回答