Add_javascript_url is not working in my XBlock

Hello,

I’m trying to develop an XBlock that shows real time data using some charts, from javascript libraries using a url. I tried two approaches.

  1. Adding the
    1. Loading the resulrces directly in the python file, in student_view, with add_javascript_url:

    html = self.resource_string(“static/html/myxblock.html”)
    context = context or {
    key: getattr(self, key)
    for key in self.editable_fields
    }
    frag = Fragment(html.format(**context))
    frag.add_css(self.resource_string(“static/css/myxblock.css”))
    frag.add_javascript(self.resource_string(“static/js/src/myxblock.js”))

    frag.add_javascript_url("//cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js")
    frag.add_javascript_url("//cdn.jsdelivr.net/gh/gitbrent/bootstrap-switch-button@1.1.0/js/bootstrap-switch-button.min.js")

    frag.initialize_js(‘MyXBlock’)

    return frag

    In both cases, I get the following errors:
    The resource from //cdn.jsdelivr.net/gh/gitbrent/bootstrap-switch-button@1.1.0/js/bootstrap-switch-button.min.js" was blocked due to MIME type (“text/html” mismatch (X-Content-Type-Options: nosniff).

    I also tried to specify the MIME type with ‘application/javascript’, but I get the same result.

    Hope you can guide me in adding external javascript files from url’s

    Thanks
    Joan

Hi @Joan_Rodriguez,

There’s a simple reason for that - you have the wrong URL.

Just click on https://cdn.jsdelivr.net/gh/gitbrent/bootstrap-switch-button@1.1.0/js/bootstrap-switch-button.min.js in your browser and you’ll see that it’s not a JavaScript file, it’s an HTML document with an error message.

Hopefully once you get the right URL it will be working fine :slight_smile:

Hi @braden,

Thanks, that fixed the issue for that js file. However, let me go back to this one:

frag.add_javascript_url("//cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js")

This script is loaded correctly, it basically implements some fancy charts from chartjs.
In the javascript file of the xblock e.g. “frag.add_javascript(self.resource_string(“static/js/src/myxblock.js”))”, i define the chart by following the documentation in Introduction · Chart.js documentation, as.

let plot = new Chart(…).

The problem is when I load the xblock in the demo course, where I get an error in the console:

Uncaught ReferenceError : Chart is not defined.

Thanks a lot
Joan

I’m not sure, it could be any number of things. Perhaps in the HTML, your JavaScript is appearing before the <script> tag that loads Chart.js, so Chart.js is not loaded yet?

Instead of loading it via add_javascript_url, you could try using requirejs, which is available already in the XBlock JS runtime environment.

window.RequireJS.require(['https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.min.js'], () => {
    const plot = new Chart(...);
});

Even better would be to use Webpack or Rollup or TypeScript to transpile and bundle your XBlock’s code and dependencies into a single minified, tree-shaken file.

Hello @braden,

Using RequireJS fixed the issue, thanks!.

However, I was learning about what you said about transpiling the XBlock with Webpack or others, but I can’t manage to understand how it works. What is exactly the process of transpiling the XBlock and create a tree-shaken file? Where can I find documentation on how to do it?

My problem now is that my XBlock works good in the SDK server but not in the LMS. For example, my javascript file should enter this method when the page is loaded, using

window.onload = async function() {
console.log(“Some functionality”)
await someFunction()
}

Thanks a lot
Joan

It’s how I think XBlock JavaScript should be done going forward, but as far as I know, nobody has yet done it that way. So it would require some innovation and exploration. I’ve been hoping to prepare a demo of that (or convert an existing XBlock to do that) one of these days, but it’s unfortunately been on the back burner for a while.

That said, Ramshackle is a Studio plugin that I created, and it uses TypeScript to do that (transpile the source code and bundle in all the requirements like React, producing a single .js file; it’s done with the make js-watch command). So I’d likely use a very similar approach for an XBlock.

Using window.onload in that manner is not dependable, because the XBlock is often (always?) loaded “lazily”, after the window.onload event has already fired.

Instead, your JavaScript code should contain some main function like:

function MyBlock(runtime, element, configuration) {
    console.log("Some functionality");
}

and then in the python student_view, you tell the runtime to run that function when the block loads, like this:

    configuration = {}  # Optional data to pass from python to JavaScript
    fragment.initialize_js('MyBlock', configuration)
    return fragment

Check the source code of almost any XBlock for an example.