Reputation: 41
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
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