I'm tasked with creating a Knockout Components based UI using Typescript.
This is something I've done hundreds of times in vanilla JS, but I just can't seem to get TS to generate a JS module in the correct format to be consumed by Require JS.
Ideally, what I'd like is for Typescript to generate identical output to that written in JS, but right now I'd just like to get things working.
This is the Javascript that I'm trying to get TS to generate, this is Javascript from a DIFFERENT project that does not use TS, and in that project when the JS is in this format, everything works fine.
define(["knockout", "text!./menubar.html"], function (ko, menubarTemplate)
{
function menubarViewModel()
{
var self = this;
self.menuBrand = ko.observable("Menu Brand");
self.menuItems = ko.observableArray([]);
self.load();
return self;
}
menubarViewModel.prototype.load = function ()
{
var self = this;
$.getJSON("data/menudata.json", function (data)
{
self.menuItems(data);
});
};
return { viewModel: menubarViewModel, template: menubarTemplate };
});
In my actual JS file that uses the component all I need to do is:
define(["jquery", "knockout", "bootstrap"], function ($, ko)
{
ko.components.register("menubar",
{
require: "application/components/menubar"
});
ko.applyBindings();
});
The HTML for the menubar component is just a simple chunk of plain HTML markup sprinkled with "data-bind" attributes where needed to inject the data into the component.
As I say, this JavaScript version works perfectly, but the client I'm working for at the moment wants this in Typescript, so the first challenge I need to tackle is how to return
return { viewModel: menubarViewModel, template: menubarTemplate };
from a typescript module.
Iv'e had a small amount of success, for instance if I do:
import ko = require("knockout");
module HelloComponent {
export class HelloViewModel {
helloText = ko.observable<string>("Hello World");
}
}
That produces a JS class, that ko tries to load, but complains that it has no template.
This says to me that if I can take the TS class above and export the require text HTML from the same class, then I might just make this work.
If I further expand that class as follows:
import ko = require("knockout");
import helloTemplate = require("text!application/components/hello.html");
module HelloComponent {
export class HelloViewModel {
helloText = ko.observable<string>("Hello World");
}
var tmp = helloTemplate;
}
I've been trying to solve this for a couple of days now, and most of the experimentation I've tried has either failed, or appears to run in the chrome debugger, but produces no output in the component.
There are dozens of posts here on SO, but none of them apply to Knockout Components, all the others apply to page level standard binding, which is different from KO components, it's the same scenario with the various blog posts I've been reading.
If anyone has an insight on how to implement this as per the advice in the KnockoutJS docs but using TS rather than JS, then I'd love to hear your ideas.
After changing one of my components to match 'James Brantly' s answer, I now see the following in Visual Studio:
Even with the errors shown above, I've now put together several components, all using the same methodology, and everything works perfectly.
Visual studio still flags these files as having errors, but it still allows me to compile the project, and Typescript still does what it needs to and compiles to JavaScript.
At this point, am marking the question as answered, as the initial question has been solved.
I think the key to your question is that you want the module to return something like { viewModel: menubarViewModel, template: menubarTemplate };
. You do that like this:
import menubarTemplate = require('text!./menubar.html');
class MenubarViewModel{
}
export = {
viewModel: MenubarViewModel,
template: menubarTemplate
}