Calling typescript function from HTML using a module system
Asked Answered
F

1

12

I would like to call a TypeScript (ultimetely JavaScript) function from HTML. The Problem is that I would also like to use a module System (systemjs, commonjs etc.) and also webpack.

Here is an example:

example.ts:

export class Example {
    myFunction() {
        alert('Test');
    }
}

example2.ts:

export function doSomething() {
    alert('Test2');
}

example.html:

<html>
    <head>
        <script src="path/to/the/compiled/js/file.js"></script>
        <script>
            $(document).ready(function() {
                $('#test-btn').click(function(){
                    var example = new Example(); //error1
                    example.myFunction();
                });
            });
        </script>
    </head>
    <body>
        <button id="test-btn">Click me</button>
        <a href="javascript:doSomething()">Click me too</a> <!-- error2 -->
    </body>
</html>

The error comments indicate where the Errors happen: The exported class/function is not found because a module System is used. If I look into the compiled js files I can see that the class and function declarations are put within some module functions, so they are not accessible from HTML.

Currently I am using a Workaround by moving the entire $(document).ready... section into a TypeScript file. But for the <a href... example I don't have any Workaround.

So my final question: Is it actually possible to call a TypeScript function/class from HTML and using a module System?

Faroff answered 10/1, 2018 at 9:18 Comment(3)
you could have a separate file for global functions that listen to message events. those would broadcast to your typescript app.Fourdimensional
@DanielLizik Thanks that's a good idea! But I think it is just another hack. Is there no "proper" way of doing this?Faroff
IMO this is the "proper" way. it keeps your window object unpolluted and separates your concerns.Fourdimensional
B
11

Yes. There are 2 ways that webpack supports this

1) Output using library option. This will write the exports of the entry file to a namespace variable in global scope of your choosing

myEntryFile.ts

import {Example} from './example'
import {doSomething} from './example2'

export {
  Example,
  doSomething
}

webpack.config.js

output: {
  //... your normal output config her
  library: 'MyExposedNamespace'
}

This will now be available at window.MyExposedNamespace

window.MyExposedNamespace.doSomething()
const myExample = new window.MyExposedNamespace.Example()

For more advanced usage you can also change the scope that the namespace is applied to via libraryTarget so doesn't pollute the global scope

2) Using expose-loader

webpack.config.js

module: {
  rules: [
    { test: /example\.ts$/, 
      use: [
       { loader: 'expose-loader', options: 'Example'},
       'awesome-typescript-loader' // must transpile as file is typescript
      ]
    },
    { test: /example2\.ts$/, 
      use: [
        { loader: 'expose-loader', options: 'doSomeThing'},
        'awesome-typescript-loader'
      ]
    }
  ]
}

You must also remember to import these files in your project somewhere, otherwise webpack will ignore them. You don't have to actually use them, its just for webpack to trace dependencies. Most logical place is your entry file

myEntryFile.ts

import './example'
import './example2'

These can then be called via the global scope (in browser this is of course window)

window.doSomeThing()
const myExample = new window.Example()

This one may become trickier due to the typescript transpilation step also needing to occur before being exposed, which depending on your other config, could get more complex than the simple version above

Therefore I'd recommend the first solution as the easiest

Biancabiancha answered 10/1, 2018 at 10:45 Comment(2)
Very nice, thank you very much! That was exactly what I was looking for. Just one more question: Is it also possible that webpack Exports external libraries too? So that I can use e.g. jquery in my html file too?Faroff
yep if you have it installed as a node module you can add add a loader for exposing it too { test: require.resolve('jquery'), use: { loader: 'expose-loader', options: 'myExposedJqueryInstance'} }Biancabiancha

© 2022 - 2024 — McMap. All rights reserved.