Monthly Archives: October 2014

Knockout Extender: observe

ko.extenders.observe = function (target, source) {
    target(source);
    var result = ko.computed({
        read: function () {
            return target()();
        },
        write: function (value) {
            target()(value);
        }
    });
    result.observe = function (source) {
        target(source);
    };
    return result;
};

var x = ko.observable(10);
var y = ko.observable(20);
var z = ko.observable().extend({ observe: x });
console.log(z());
x(11);
console.log(z());
y(21);
console.log(z());
z.observe(y);
console.log(z());
x(12);
console.log(z());
y(22);
console.log(z());
z(30);
console.log(z());
console.log(y());

Output:

10
11
11
21
21
22
30
30

Knockout Extender: numeric

function log(value) {
    console.log(typeof value + ": " + value);
}

ko.extenders.numeric1 = function (target) {
    return ko.computed({
        read: target,
        write: function (value) {
            var number = parseFloat(value);
            target(isNaN(number) ? value : number);
        }
    });
};

var x = ko.observable().extend({ numeric1: {} });
x(1);
log(x());
x("2");
log(x());
x("three");
log(x());

ko.extenders.numeric2 = function (target) {
    target.value = ko.computed(function () {
        return parseFloat(target());
    });
    return target;
};

var y = ko.observable().extend({ numeric2: {} });
y("4");
log(y());
log(y.value());
y("five");
log(y());
log(y.value());

Output:

number: 1
number: 2
string: three
string: 4
number: 4
string: five
number: NaN

Knockout Extender: coalesce

ko.extenders.coalesce = function (target, defaultValue) {
    var result = ko.computed({
        read: function () {
            return target() === undefined ? ko.unwrap(defaultValue) : target();
        },
        write: function (value) {
            target(value);
        }
    });
    result.defaultValue = ko.computed(function () {
        return ko.unwrap(defaultValue);
    });
    return result;
};

var x = ko.observable().extend({ coalesce: 1 });
console.log(x());
x(2);
console.log(x());
console.log(x.defaultValue());
x(undefined);
console.log(x());
var y = ko.observable(3);
var z = ko.observable().extend({ coalesce: y });
console.log(z());
y(4);
console.log(z());

Output:

1
2
1
1
3
4

Knockout Extender: cache

ko.extenders.cache1 = function (target) {
    target.cached = ko.observable(target.peek());
    target.cache = function () {
        target.cached(target());
    };
    return target;
};

var x = ko.observable(1).extend({ cache1: {} });
x(2);
console.log(x());
console.log(x.cached());
x.cache();
console.log(x.cached());

ko.extenders.cache2 = function (target) {
    var cached = ko.observable(target.peek());
    var result = ko.computed({
        read: cached,
        write: function (value) {
            target(value);
        }
    });
    result.current = target;
    result.cache = function () {
        cached(target());
    };
    return result;
};

var y = ko.observable(3).extend({ cache2: {} });
y(4);
console.log(y.current());
console.log(y());
y.cache();
console.log(y());

Output:

2
1
2
4
3
4

Observable Values in C#

using System;

public class Observable<T>
{
    public class ChangingEventArgs : EventArgs
    {
        public T OldValue { get; private set; }
        public T NewValue { get; private set; }
        public bool Cancel { get; set; }

        public ChangingEventArgs(T oldValue, T newValue)
        {
            OldValue = oldValue;
            NewValue = newValue;
            Cancel = false;
        }
    }

    public class ChangedEventArgs : EventArgs
    {
        public T Value { get; private set; }

        public ChangedEventArgs(T value)
        {
            Value = value;
        }
    }

    public event EventHandler<ChangingEventArgs> Changing;
    public event EventHandler<ChangedEventArgs> Changed;

    protected T value;

    public T Value
    {
        get
        {
            return value;
        }
        set
        {
            if (this.value == value)
            {
                return;
            }
            ChangingEventArgs e = new ChangingEventArgs(this.value, value);
            OnChanging(e);
            if (e.Cancel)
            {
                return;
            }
            this.value = value;
            OnChanged(new ChangedEventArgs(this.value));
        }
    }

    public Observable() { }

    public Observable(T value)
    {
        this.value = value;
    }

    protected virtual void OnChanging(ChangingEventArgs e)
    {
        EventHandler<ChangingEventArgs> handler = Changing;
        if (handler == null)
        {
            return;
        }
        handler(this, e);
    }

    protected virtual void OnChanged(ChangedEventArgs e)
    {
        EventHandler<ChangedEventArgs> handler = Changed;
        if (handler == null)
        {
            return;
        }
        handler(this, e);
    }
}