Zack
Zack

Reputation: 69

Tabulator not loading all the way

I have an .net-core application. For some reason the Tabulator <script> inclusion is delayed.

Here is my Layout.cshtml

<!DOCTYPE html>
<div class="container body-content">
    @RenderBody()
</div>

<head>
    <link href="https://redacted.com/tabulator.min.css" rel="stylesheet" />
</head>

<script type="text/javascript" src="https://redacted.com/tabulator.min.js" >
</script>
<script type="module" src="~/js/site.js" asp-append-version="true">
    import {Tabulator, FilterModule, HtmlTableImportModule, SelectRowModule} from 'tabulator-tables';
</script>

<script defer>
    // This results in 'undefined'
    console.log("Tabulator is not defined immediately: ", this.Tabulator);
    // Giving it 100ms seems to always result in defined. Using just 1ms results in 'undefined'
    setTimeout(function() { console.log("Tabulator is defined after .1 seconds: ", this.Tabulator) } , 100); 
</script>

I can work around this by using the setTimeout function, but that is a hack I would like to avoid. What am I doing wrong?

Upvotes: 0

Views: 967

Answers (1)

Besworks
Besworks

Reputation: 4513

You have some invalid syntax in your example:

<script type="module" src="~/js/site.js" asp-append-version="true">
    import {Tabulator, FilterModule, HtmlTableImportModule, SelectRowModule} from 'tabulator-tables';
</script>

A script element with an src attribute cannot have content inside of it.

Also, modules are self-contained. Variables and imports declared in a module are not accessible from other scopes. It's likely that the module you are importing creates a reference in the global scope but that would be a specific feature of that library and cannot be relied on as a general rule.

If you were to do something like this :

<script type="module">
    import { Tabulator } from 'tabulator-tables';
    console.log(Tabulator);
</script>

You would find that Tabulator is, in fact, available immediately. Though, this reference would not be available outside of this script. If you need access to that library outside of it's current scope you would need to assign it to the global scope yourself.

<script type="module">
  import { Tabulator } from 'tabulator-tables';
  window.Tabulator = Tabulator;
</script>
<script defer>
  console.log(Tabulator);
</script>

However, bare specifiers like from 'tabulator-tables' are not available by default in browsers so unless you are bundling your source with a tool like webpack, the above import statement would do nothing but throw an error. To import modules in this way without bundling you can use an experimental new feature called import-maps in some browsers.

<script type="importmap">
  {
    "imports": {
      "tabulator": "https://cdnjs.cloudflare.com/ajax/libs/tabulator/5.2.7/js/tabulator_esm.min.js"
    }
  }
</script>
<script type="module">
  import { Tabulator } from 'tabulator';
  console.log(Tabulator);
</script>

But, the simplest and most well supported way to achieve what you want is to just import the classes from the full script URL into each scope that needs them. Modules are only downloaded and evaluated once, so you can import as many times as necessary throughout your page without any detriment to performance.

<script type="module">
  import { Tabulator, FilterModule, HtmlTableImportModule, SelectRowModule } from 'https://cdnjs.cloudflare.com/ajax/libs/tabulator/5.2.7/js/tabulator_esm.min.js';
  console.log(Tabulator, FilterModule, HtmlTableImportModule, SelectRowModule);
</script>
<script type="module">
  import { Tabulator } from 'https://cdnjs.cloudflare.com/ajax/libs/tabulator/5.2.7/js/tabulator_esm.min.js';
  console.log(Tabulator);
</script>

Upvotes: 1

Related Questions