r3plica
r3plica

Reputation: 13367

Knockout JS options binding with selected option

I have this mind boggling issue. I have 2 dropdowns on my page, which look like this:

<div class="form-group">
    <label class="control-label">Current competition</label>
    <select class="form-control" data-bind="options: competitions, optionsText: 'Name', value: selectedCompetition, optionsCaption: 'Choose...'"></select>
</div>

<div class="form-group">
    <label class="control-label">Style</label>
    <select class="form-control" data-bind="options: styles, optionsText: 'Name', value: selectedStyle, optionsCaption: 'Choose...'"></select>
</div>

the competitions one works perfectly, but the styles one never selects anything but Choose.... My view model has an initialise method which looks like this:

function init() {
    $.get(competitionsUrl)
        .done(function (data) {
            $.each(data, function (i, competition) {                    
                self.competitions.push(competition);

                if (competition.Current) {
                    self.selectedCompetition(competition);
                    self.selectedStyle(competition.Style);
                }
            });
        })
        .always(function () {
            self.loading(false);
        })
        .fail(common.handleError);

    $.get(stylesUrl)
        .done(function (data) {
            $.each(data, function (i, style) {
                self.styles.push(style);
            });
        })
        .fail(common.handleError);
};

Now, this is where the funny part comes in. If I put a console.log(self.selectedStyle()) directly after I set it, I get undefined in the console. It took awhile, but I realised that if I change my Style select to this:

<div class="form-group">
    <label class="control-label">Style</label>
    <select class="form-control" data-bind="options: styles, optionsText: 'Name'"></select>
</div>

Then my console returns the correct selected style. So, what is happening is that the select box is actually setting my selectedStyle to undefined. Why it is happening only on the style dropdown and not on the competitions baffles me.

Has anyone experienced this before and perhaps knows of a solution?

Just in case, this is my entire view model.

function SettingsViewModel(app, dataModel) {
    var self = this,
        competitionsUrl = "/api/competitions",
        stylesUrl = "/api/styles";

    // Private operations
    function init() {
        $.get(competitionsUrl)
            .done(function (data) {
                $.each(data, function (i, competition) {                    
                    self.competitions.push(competition);

                    if (competition.Current) {
                        self.selectedCompetition(competition);
                        self.selectedStyle(competition.Style);
                    }
                });
            })
            .always(function () {
                self.loading(false);
            })
            .fail(common.handleError);

        $.get(stylesUrl)
            .done(function (data) {
                $.each(data, function (i, style) {
                    self.styles.push(style);
                });
            })
            .fail(common.handleError);
    };

    function uploadProgress(e, progressBar) {
        if (e.lengthComputable) {
            var percentComplete = Math.round(e.loaded * 100 / e.total).toString();

            common.setProgressValue(progressBar, percentComplete);
        }
        else {
            toastr["warning"](uploadProgressErrorMessage);

            common.setProgressValue(progressBar, 0);
        }
    };

    function uploadComplete(file, filePath) {
        console.log(file);
        console.log(filePath);
    };

    // Data
    self.styles = ko.observableArray();
    self.competitions = ko.observableArray();
    self.selectedStyle = ko.observable();
    self.selectedCompetition = ko.observable();

    // State
    self.loading = ko.observable(true);
    self.saving = ko.observable(false);

    // Operations
    self.selectedCompetition.subscribe(function (vm, event) {
        if (!self.loading()) {
            self.selectedCompetition().Current = true;

            self.save();
        }
    });

    self.selectedStyle.subscribe(function (vm, event) {
        if (vm) {
            self.selectedCompetition().StyleId = vm.Id;
        }
    });

    self.uploadLogo = function (e) {        
        if (e.target.files && e.target.files[0]) {
            var file = e.target.files[0];

            common.getUrl().done(function (url) {
                common.generateFolders(0).done(function (folders) {
                    common.uploadFile(url, file, folders, ".logo-progress", uploadProgress, uploadComplete);
                });
            }).fail(function () {
                toastr["error"]("Failed to get the upload url.");
            });
        }
    };

    self.save = function () {
        self.saving(true);

        console.log(self.selectedCompetition());

        $.ajax({
            url: competitionsUrl,
            method: "POST",
            data: self.selectedCompetition()
        }).done(function () {
            // Do nothing
        }).always(function () {
            self.saving(false);
        }).fail(common.handleError);
    };

    init();

    return self;
};

app.addViewModel({
    name: "Settings",
    bindingMemberName: "settings",
    factory: SettingsViewModel
});

Any help would be greatly appreciated as always :)

Upvotes: 0

Views: 161

Answers (1)

r3plica
r3plica

Reputation: 13367

As Hans said, this is because of how I am populating my styles. I changed my init method to this:

function init() {
    $.get(competitionsUrl)
        .done(function (data) {
            var styleId = 0;

            $.each(data, function (i, competition) {                    
                self.competitions.push(competition);

                if (competition.Current) {
                    self.selectedCompetition(competition);
                    styleId = competition.Style.Id;
                }
            });

            $.get(stylesUrl)
                .done(function (data) {
                    $.each(data, function (i, style) {
                        self.styles.push(style);

                        if (style.Id == styleId) {
                            self.selectedStyle(style);
                        }
                    });
                })
                .always(function () {
                    self.loading(false);
                })
                .fail(common.handleError);
        })
        .fail(common.handleError);
};

which fixed the issue.

Upvotes: 1

Related Questions