Question

I'm currently using the Bing Maps KO custom binding to make using a Bing Maps control in Durandal nice and easy.

So far I've extended the custom binding to allow for an info box and that works fine as I simply have to set the value of an observable in the view model and the info box updates.

Now I would like to be able to call a function on one of the map objects contained within the custom binding.

I wish to have the map zoom to a specific point. Note that I don't want to set the maps centre point, but I wish to call the function that will handle the animating of the map to that point.

This could be achieved by having an observable field which I set in the view model, then the custom binding has it's update function called, it grabs the value, calls the method and then resets the observable back to being empty.

That feels rather hacky but should work, and it doesn't really step outside of the lines of separation you should maintain when using MVVM.

Is there another way to achieve this using function syntax so that it more correctly describes whats happening. (I'm requesting a series of actions to happen, rather than setting a value.)

Was it helpful?

Solution

One way to achieve this is using events.

You can pass the custom binding an event name and have it subscribe to that event.

Since I'm using Durandal I would normally use the built-in event system, but jsFiddle only has KnockoutJS available so the following example uses a small PubSub library in it's place.

This is example code, rather than anything that will be put in to production.

jsFiddle

//PubSub//
(function(a,c,b){if(typeof module!=="undefined"){module.exports=b(a,c)}else{if(typeof define==="function"&&typeof define.amd==="object"){define(b)}else{c[a]=b(a,c)}}})("radio",this,function(b,c){function a(d){a.$.channel(d);return a.$}a.$={version:"0.2",channelName:"",channels:[],broadcast:function(){var f,j=this.channels[this.channelName],d=j.length,g,h,e;for(f=0;f<d;f++){g=j[f];if((typeof(g)==="object")&&(g.length)){h=g[0];e=g[1]||c}h.apply(e,arguments)}return this},channel:function(d){var e=this.channels;if(!e[d]){e[d]=[]}this.channelName=d;return this},subscribe:function(){var f=arguments,j=this.channels[this.channelName],g,e=f.length,h,d=[];for(g=0;g<e;g++){d=f[g];h=(typeof(d)==="function")?[d]:d;if((typeof(h)==="object")&&(h.length)){j.push(h)}}return this},unsubscribe:function(){var g=arguments,k,h,n=this.channels[this.channelName],f=g.length,e=n.length,m=0,d;for(k=0;k<f;k++){m=0;e=n.length;for(h=0;h<e;h++){d=h-m;if(n[d][0]===g[k]){n.splice(d,1);m++}}}return this}};return a});
//PubSub//

ko.bindingHandlers.customBinding = {
    _textBoxes: {},
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var _valueAccessor = valueAccessor();

        var textBox = document.createElement("input");
        textBox.type = "text";
        element.appendChild(textBox);

        ko.bindingHandlers.customBinding._textBoxes[element.id] = textBox;

        if(_valueAccessor.clearEventName) {
            radio(_valueAccessor.clearEventName).subscribe(function() {
                _valueAccessor.text("");
            });
        }

        if(_valueAccessor.text) {
            textBox.onchange=function() { _valueAccessor.text(textBox.value); };
        }
    },
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var _valueAccessor = valueAccessor();

        if(_valueAccessor.text) {
            ko.bindingHandlers.customBinding._textBoxes[element.id].value = _valueAccessor.text();
        }
    }
};

var viewModel = {
    text: ko.observable("Inital value."),
    doClear: function() {
        radio('clear').broadcast();
    },
    doReset: function() {
        viewModel.text("Reset value.");
    }
};

ko.applyBindings(viewModel);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top