最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Knockout change event-handler - Stack Overflow

programmeradmin5浏览0评论

I am spending hours trying to get a simple event call working correctly in my durandal/knockout app.

Context

I have a list of languages that that the user can select from a select-box:

    <select class="form-control select2"
        data-bind="event: { change: app.languageChanged }, options:languages,
        optionsText:'label',
        optionsValue:'code',
        value:app.selectedLanguage"></select>

The property app.selectedLanguage is a ko.observable. I know that this works because the correct item gets pre-selected.

    this.selectedLanguage = ko.observable(options.defaultLanguage);

I also have an event-handler which listens for changes on that select box, so that I can send a message to other parts of the application that need to be informed:

    languageChanged : function(data, event) {
        console.log(data);
        console.log(event);
        console.log(this.selectedLanguage());

        app.trigger('language:change', this.selectedLanguage());
    },

The problem

  1. the first parameter 'data' does not contain the selected item, but instead contains all items (actually, it seems to be the complete current view-model).
  2. If 1. does not work, then it would be an alternative to at least get hold of the new value from the observable 'selectedLanguage'. Unfortunately that always seems to have the old value. So whenever I change the selectbox option, I always get the previously selected value.

Question

So the question is: what could I be doing wrong? I am sure that this normally works correctly and I must be missing something somewhere.

I thought I had finally understood how knockout works, but now I have come across the next issue. I would be very grateful if someone could help me on this.

EDIT [SOLVED]

Thanks to xdumaine, here is the (nice and simple) solution:

In my html template, I removed the change-event:

    <select class="form-control select2"
        data-bind="options:languages,
        optionsText:'label',
        optionsValue:'code',
        value:app.selectedLanguage"></select>

In my App view-model (that I require everywhere), I now subscribe to the ko.observable instead of listening to the event-handler:

    define([ 'durandal/app', 'underscore', 'knockout', 'myapp/myapp' ], function(app, _, ko, myapp) {

        "use strict";

        function App(options) {

            if (!(this instanceof App)) {
                throw new TypeError("App constructor cannot be called as a function.");
            }

            this.options = options || {};

            // Set the initial language.
            this.selectedLanguage = ko.observable(options.defaultLanguage);
                    // *** Subscribes to the observable ***
            this.selectedLanguage.subscribe(function(newValue) {
                console.log(newValue);
                app.trigger('language:change', newValue);
            });

            _.bindAll(this, 'getSelectedLanguage');
        }

        App.prototype = {
            constructor : App,
            getSelectedLanguage : function() {
                return this.selectedLanguage();
            }
        }

        return App;
    });

This code has therefore been removed and is no longer needed:

languageChanged : function(data, event) {
    console.log(data);
    console.log(event);
    console.log(this.selectedLanguage());

    app.trigger('language:change', this.selectedLanguage());
},

Best regards, Michael

I am spending hours trying to get a simple event call working correctly in my durandal/knockout app.

Context

I have a list of languages that that the user can select from a select-box:

    <select class="form-control select2"
        data-bind="event: { change: app.languageChanged }, options:languages,
        optionsText:'label',
        optionsValue:'code',
        value:app.selectedLanguage"></select>

The property app.selectedLanguage is a ko.observable. I know that this works because the correct item gets pre-selected.

    this.selectedLanguage = ko.observable(options.defaultLanguage);

I also have an event-handler which listens for changes on that select box, so that I can send a message to other parts of the application that need to be informed:

    languageChanged : function(data, event) {
        console.log(data);
        console.log(event);
        console.log(this.selectedLanguage());

        app.trigger('language:change', this.selectedLanguage());
    },

The problem

  1. the first parameter 'data' does not contain the selected item, but instead contains all items (actually, it seems to be the complete current view-model).
  2. If 1. does not work, then it would be an alternative to at least get hold of the new value from the observable 'selectedLanguage'. Unfortunately that always seems to have the old value. So whenever I change the selectbox option, I always get the previously selected value.

Question

So the question is: what could I be doing wrong? I am sure that this normally works correctly and I must be missing something somewhere.

I thought I had finally understood how knockout works, but now I have come across the next issue. I would be very grateful if someone could help me on this.

EDIT [SOLVED]

Thanks to xdumaine, here is the (nice and simple) solution:

In my html template, I removed the change-event:

    <select class="form-control select2"
        data-bind="options:languages,
        optionsText:'label',
        optionsValue:'code',
        value:app.selectedLanguage"></select>

In my App view-model (that I require everywhere), I now subscribe to the ko.observable instead of listening to the event-handler:

    define([ 'durandal/app', 'underscore', 'knockout', 'myapp/myapp' ], function(app, _, ko, myapp) {

        "use strict";

        function App(options) {

            if (!(this instanceof App)) {
                throw new TypeError("App constructor cannot be called as a function.");
            }

            this.options = options || {};

            // Set the initial language.
            this.selectedLanguage = ko.observable(options.defaultLanguage);
                    // *** Subscribes to the observable ***
            this.selectedLanguage.subscribe(function(newValue) {
                console.log(newValue);
                app.trigger('language:change', newValue);
            });

            _.bindAll(this, 'getSelectedLanguage');
        }

        App.prototype = {
            constructor : App,
            getSelectedLanguage : function() {
                return this.selectedLanguage();
            }
        }

        return App;
    });

This code has therefore been removed and is no longer needed:

languageChanged : function(data, event) {
    console.log(data);
    console.log(event);
    console.log(this.selectedLanguage());

    app.trigger('language:change', this.selectedLanguage());
},

Best regards, Michael

Share Improve this question edited Mar 21, 2014 at 13:35 michaeldd asked Mar 21, 2014 at 13:03 michaelddmichaeldd 5142 gold badges6 silver badges19 bronze badges 6
  • 1 Maybe this could help : knockoutjs.com/documentation/unobtrusive-event-handling.html – Yabada Commented Mar 21, 2014 at 13:07
  • It's unclear what app is in your code. Is that a durandal thing or is that your viewModel? – xdumaine Commented Mar 21, 2014 at 13:13
  • Hi, it is a global ViewModel that I pass via require into all my pages: define([ 'knockout', 'myapp/myapp' ], function(ko, myapp) {. I then make it available to the view like this: return { app: myapp.app, ...}. – michaeldd Commented Mar 21, 2014 at 13:18
  • @michaeldd Michael, that goes back to one of the recommendations I made: knockout-postbox. Ryan Niemeyer has built some great functionality on top of subscribing directly to observables that's just as easy to use. While we use postal.js for our client-side message bus, we do you direct subscriptions for internal notifications, and when we need to get that close to the metal. – user3174746 Commented Mar 21, 2014 at 17:10
  • Yes, thanks. I suppose in the question that you are referring to (stackoverflow.com/questions/22542051/…) I was more concerned in getting the language-change-event being "noticed" in the right places. It did not occur to me at that point that I was having this issue. I thought the actual handling of the event was working fine and from there it was just a matter of passing the message on. This morning I realised that that was unfortunately not the case and I had not quite understood the concept of the knockout-subscribe yet. – michaeldd Commented Mar 21, 2014 at 19:44
 |  Show 1 more comment

1 Answer 1

Reset to default 19

Why bind to the select change event instead of just subscribing to the selectedLanguage?

var self = this;
self.selectedLanguage = ko.observable();
self.selectedLangauge.subscribe(function(newValue) {
    console.log(newValue);
    app.trigger('language:change', newValue);
});

If you want to do it like you have it, know this: event bindings in knockout always get a reference to the viewModel as the first parameter, and the event data as the second, so you could would have to inspect the event to get the target and extract the value if you're doing it that way. The reason 2 is not working is that your change event is firing before the knockout observable is notified, so you get timing issues. This could have different behavior in different browsers.

I'd recommend sticking to observable subscriptions, instead of using DOM events, whenever possible.

发布评论

评论列表(0)

  1. 暂无评论