Andrew Jones
Andrew Jones

Reputation: 41

Knockout Component with Typescript

i'm trying to implement typescript for our SPA using components, but i can't seem to get a handle on the viewmodel that's in the component.

When we did this with Javascript and Knockout, i'd declare an observable property to pass in as a callback, and have the component viewmodel populate the observable with itself, giving me access to any function as part of that viewmodel, for example:

Component:

      ko.components.register('MessageBox', {
    viewModel: function (params) {
        init: {
            var self = this;

            // Button Events
            self.OkButton_Click = function () {
                self.FireResponse(self.Response.Ok);
            };
            self.CancelButton_Click = function () {
                self.FireResponse(self.Response.Cancel);
            };
            self.YesButton_Click = function () {
                self.FireResponse(self.Response.Yes);
            }
            self.NoButton_Click = function () {
                self.FireResponse(self.Response.No);
            }
            self.AbortButton_Click = function () {
                self.FireResponse(self.Response.Abort);
            }
            self.BackButton_Click = function () {
                self.FireResponse(self.Response.Back);
            }
            self.Custom1Button_Click = function () {
                self.FireResponse(self.Response.Custom1);
            }
            self.Custom2Button_Click = function () {
                self.FireResponse(self.Response.Custom2);
            }
            // Enums
            self.Response = {
                Ok: "Ok",
                Cancel: "Cancel",
                Yes: "Yes",
                No: "No",
                Abort: "Abort",
                Back: "Back",
                Custom1: "Custom1",
                Custom2: "Custom2"
            }
            self.Config = {
                OKCancel: "OkCancel",
                OKBack: "OKBack",
                OK: "Ok",
                Cancel: "Cancel",
                YesNo: "YesNo",
                YesNoCancel: "YesNoCancel",
                Abort: "Abort",
                Custom1: "Custom1",
                Custom1Custom2: "Custom1Custom2",
                Custom1Cancel: "Custom1Cancel",
                Custom1Custom2Cancel: "Custom1Custom2Cancel",
                None: 'None'
            }

            // Functions
            self.FireResponse = function (responseCode) {
                self.ResetButtons();
                $.event.trigger({
                    type: "messageBoxResponse",
                    message: responseCode,
                    time: new Date()
                });

                if (typeof self.callback != 'undefined' && self.callback)
                    self.callback(responseCode);
            }
            self.ResetButtons = function () {
                self.ShowOk(false);
                self.ShowCancel(false);
                self.ShowYes(false);
                self.ShowNo(false);
                self.ShowAbort(false);
                self.ShowingMessageBox(false);
                self.ShowCustom1(false);
                self.ShowCustom2(false);
            }

            // Observables
            self.Text = ko.observable("This is a test message from the message box VM");
            self.SubText = ko.observable("");
            self.ShowingMessageBox = ko.observable(false);

            self.ShowOk = ko.observable(false);
            self.ShowCancel = ko.observable(false);
            self.ShowYes = ko.observable(false);
            self.ShowNo = ko.observable(false);
            self.ShowAbort = ko.observable(false);
            self.ShowBack = ko.observable(false);
            self.ShowCustom1 = ko.observable(false);
            self.ShowCustom2 = ko.observable(false);

            self.Custom1Text = ko.observable("Custom1");
            self.Custom2Text = ko.observable("Custom2");
            self.callback = null;

            // Accessable Functions
            self.ShowWithCustom = function (configuration, message, subMessage, custom1Text, custom2Text) {
                if (IsNullOrUndefined(custom1Text) == false) {
                    self.Custom1Text(custom1Text);
                }

                if (IsNullOrUndefined(custom2Text) == false) {
                    self.Custom2Text(custom2Text);
                }

                self.Show(configuration, message, subMessage);
            }
            self.Show = function (configuration, message, subMessage, callback) {
                self.Text(message);
                self.SubText(subMessage);
                self.callback = callback;
                switch (configuration) {
                    case self.Config.OKCancel:
                        self.ShowOk(true);
                        self.ShowCancel(true);
                        self.ShowingMessageBox(true);
                        break;
                    case self.Config.OKBack:
                        self.ShowOk(true);
                        self.ShowBack(true);
                        self.ShowingMessageBox(true);
                        break;
                    case self.Config.OK:
                        self.ShowOk(true);
                        self.ShowingMessageBox(true);
                        break;
                    case self.Config.Cancel:
                        self.ShowCancel(true);
                        self.ShowingMessageBox(true);
                        break;
                    case self.Config.YesNo:
                        self.ShowYes(true);
                        self.ShowNo(true);
                        self.ShowingMessageBox(true);
                        break;
                    case self.Config.YesNoCancel:
                        self.ShowYes(true);
                        self.ShowNo(true);
                        self.ShowCancel(true);
                        self.ShowingMessageBox(true);
                        break;
                    case self.Config.Abort:
                        self.ShowAbort(true);
                        self.ShowingMessageBox(true);
                        break;
                    case self.Config.Custom1Cancel:
                        self.ShowCancel(true);
                    case self.Config.Custom1:
                        self.ShowCustom1(true);
                        self.ShowingMessageBox(true);
                        break;
                    case self.Config.Custom1Custom2Cancel:
                        self.ShowCancel(true);
                    case self.Config.Custom1Custom2:
                        self.ShowCustom1(true);
                        self.ShowCustom2(true);
                        self.ShowingMessageBox(true);
                        break;
                    case self.Config.None:
                        self.ShowingMessageBox(true);
                        break;
                }
            }

            params.callback(self);
        }
    },
    template: '<div data-bind="ElementShow: ShowingMessageBox">\
                                <blackout/>\
                                    <div class="messageBoxWrapper">\
                                        <div class="messageBoxCell" data-bind="text:Text"></div>\
                                        <div class="messageBoxCell" style="font-weight:normal;" data-bind="text:SubText"></div>\
    <div class="messageBoxCell">\
                                        <div class="yesButton" data-bind="click: OkButton_Click, visible: ShowOk">OK</div>\
                                        <div class="yesButton" data-bind="click: YesButton_Click, visible: ShowYes">Yes</div>\
                                        <div class="yesButton" data-bind="click: Custom1Button_Click, visible: ShowCustom1, text:Custom1Text"></div>\
                                        <div class="yesButton" data-bind="click: Custom2Button_Click, visible: ShowCustom2, text:Custom2Text"></div>\
                                        <div class="noButton" data-bind="click: NoButton_Click, visible: ShowNo">No</div>\
                                        <div class="cancelButton" data-bind="click:AbortButton_Click, visible: ShowAbort">Abort</div>\
                                        <div class="cancelButton" data-bind="click:BackButton_Click, visible: ShowBack">Back</div>\
                                        <div class="cancelButton" data-bind="click: CancelButton_Click, visible: ShowCancel">Cancel</div>\
                                    </div>\
    </div>\
                            </div>'    });

On Page Binding

        <div data-bind="component: { name: 'MessageBox', params: { callback: $root.MsgCallBack } }"></div>

The problem i've got is if I try a similar approach in typescript i get undefined errors, i've done a bit crawling over google and i've struggle to find anything that gives me much direction on this.

I've tried:

a) declaring an Knockoutobservable property int typescript and binding that to my component

b) instantiating an instance of my component and accessing the properties, however the this doesn't tie up to the instance i have on page.

Typescript component:

import * as i from "iComponent";
import * as ko from "knockout";
import * as c from "../../Tools/Common";   

export module MsgboxMod {


    export enum Response {
        Ok,
        Cancel,
        Yes,
        No,
        Abort,
        Back,
        Custom1,
        Custom2
    }

    export enum Config {
        OKCancel,
        OKBack,
        OK,
        Cancel,
        YesNo,
        YesNoCancel,
        Abort,
        Custom1,
        Custom1Custom2,
        Custom1Cancel,
        Custom1Custom2Cancel,
        None
    }


    export class MessageBox implements i.iComponent {

        // KO Wrappers
        ComputedBoolBuilder: c.CommonMod.Computed<boolean>
        Com: c.CommonMod.JQEvent;

        // Computed Variables
        ShowOk: KnockoutComputed<boolean>;
        ShowCancel: KnockoutComputed<boolean>;
        ShowYes: KnockoutComputed<boolean>;
        ShowNo: KnockoutComputed<boolean>;
        ShowAbort: KnockoutComputed<boolean>;
        ShowBack: KnockoutComputed<boolean>;
        ShowCustom1: KnockoutComputed<boolean>;
        ShowCustom2: KnockoutComputed<boolean>;

        // Observable Properties
        ShowMe: KnockoutObservable<boolean>;
        Text: KnockoutObservable<string>;
        SubText: KnockoutObservable<string>;
        Custom1Text: KnockoutObservable<string>;
        Custom2Text: KnockoutObservable<string>;

        Configuration: KnockoutObservable<Config>;



        constructor() {
            this.ComputedBoolBuilder = new c.CommonMod.Computed<boolean>(this);
            this.Com = new c.CommonMod.JQEvent("messageBoxResponse");
            this.WireUp();
            this.Init();
        }

        Init = function () {
            this.ShowMe = ko.observable(false);
            this.Text = ko.observable("Init mode");
            this.SubText = ko.observable("");
            this.Custom1Text = ko.observable("");
            this.Custom2Text = ko.observable("");
            this.Configuration = ko.observable(Config.OK);
        }

        // Generic Functions
        Show = function (text: string) {
            this.ShowMe(true);
            this.Text(text);
        }

        Hide = function () {
            this.ShowMe(false);
        }

        Reset = function () {

        }

        private WireUp = function () {
            this.ShowOk = this.ComputedBoolBuilder.Wire(this.ShowOKButton);
            this.ShowCancel = this.ComputedBoolBuilder.Wire(this.ShowCancelButton);
            this.ShowYes = this.ComputedBoolBuilder.Wire(this.ShowYesButton);
            this.ShowNo = this.ComputedBoolBuilder.Wire(this.ShowNoButton);
            this.ShowAbort = this.ComputedBoolBuilder.Wire(this.ShowAbortButton);
            this.ShowBack = this.ComputedBoolBuilder.Wire(this.ShowBackButton);
            this.ShowCustom1 = this.ComputedBoolBuilder.Wire(this.ShowCustom1Button);
            this.ShowCustom2 = this.ComputedBoolBuilder.Wire(this.ShowCustom2Button);
        }

        FireResponse(responseCode: string) {
            this.Reset();
            this.Com.FireResponse(responseCode);
            this.Hide();
        }

        // Click Functions
        OkButton_Click = function () {
            this.FireResponse("OK");
        }
        CancelButton_Click = function () {
            this.FireResponse("Cancel");
        };
        YesButton_Click = function () {
            this.FireResponse("Yes");
        }
        NoButton_Click = function () {
            this.FireResponse("No");
        }
        AbortButton_Click = function () {
            this.FireResponse("Abort");
        }
        BackButton_Click = function () {
            this.FireResponse("Back");
        }
        Custom1Button_Click = function () {
            this.FireResponse("Custom1");
        }
        Custom2Button_Click = function () {
            this.FireResponse("Custom2");
        }

        // Calculation Function
        ShowOKButton = function (): boolean {
            switch (this.Configuration()) {
                case Config.OKBack:
                case Config.OK:
                case Config.OKCancel:
                    return true;
                default:
                    return false;
            }
        }
        ShowCancelButton = function (): boolean {
            switch (this.Configuration()) {
                case Config.Cancel:
                case Config.OKCancel:
                case Config.Custom1Cancel:
                case Config.Custom1Custom2Cancel:
                case Config.YesNoCancel:
                    return true;
                default:
                    return false;
            }
        }
        ShowYesButton = function (): boolean {
            switch (this.Configuration()) {
                case Config.YesNo:
                case Config.YesNoCancel:
                    return true;
                default:
                    return false;
            }
        }
        ShowNoButton = function (): boolean {
            switch (this.Configuration()) {
                case Config.YesNo:
                case Config.YesNoCancel:
                    return true;
                default:
                    return false;
            }
        }
        ShowAbortButton = function (): boolean {
            switch (this.Configuration()) {
                case Config.Abort:
                    return true;
                default:
                    return false;
            }
        }
        ShowBackButton = function (): boolean {
            switch (this.Configuration()) {
                case Config.OKBack:
                    return true;
                default:
                    return false;
            }
        }
        ShowCustom1Button = function (): boolean {
            switch (this.Configuration()) {
                case Config.Custom1:
                case Config.Custom1Cancel:
                    return true;
                default:
                    return false;
            }
        }
        ShowCustom2Button = function (): boolean {
            switch (this.Configuration()) {
                case Config.Custom1Custom2:
                case Config.Custom1Custom2Cancel:
                    return true;
                default:
                    return false;
            }
        }
    }
}

HTML Template:

<div data-bind="visible: ShowMe">
    <blackout/>
        <div class="messageBoxWrapper">
            <div class="messageBoxCell" data-bind="text: Text"></div>
            <div class="messageBoxCell" style="font-weight:normal;" data-bind="text: SubText"></div>
            <div class="messageBoxCell">
            <div class="yesButton" data-bind="click: OkButton_Click, visible: ShowOk()">OK</div>
            <div class="yesButton" data-bind="click: YesButton_Click, visible: ShowYes()">Yes</div>
            <div class="yesButton" data-bind="click: Custom1Button_Click, visible: ShowCustom1(), text:Custom1Text"></div>
            <div class="yesButton" data-bind="click: Custom2Button_Click, visible: ShowCustom2(), text:Custom2Text"></div>
            <div class="noButton" data-bind="click: NoButton_Click, visible: ShowNo()">No</div>
            <div class="cancelButton" data-bind="click:AbortButton_Click, visible: ShowAbort()">Abort</div>
            <div class="cancelButton" data-bind="click:BackButton_Click, visible: ShowBack()">Back</div>
            <div class="cancelButton" data-bind="click: CancelButton_Click, visible: ShowCancel()">Cancel</div>
        </div>
    </div>
</div>

Registration (at the moment i do this just before calling ko.applyBindings for my parent VM)

ko.components.register("MessageBox", {
    viewModel: msg.MsgboxMod.MessageBox,
    template: { require: "text!TypeScript/Views/MessageBox.html" }
});

As far as i can tell my component binds up fine, I just can't figure out how to get hold of the methods on the instance of bound to the page, for example i need to be able to call "Show" and "Hide" any help would be fantastic!

Upvotes: 1

Views: 764

Answers (1)

Andrew Jones
Andrew Jones

Reputation: 41

Ok, this turned out to be a misconception with where i was trying to test my code (by calling a method on my VM just after applying bindings). The point where i was making the call was before the component had constructed. I shuffled things around a little and all sorted!

Upvotes: 0

Related Questions