Skip to content

Using SCOBot with AngularJS and Angular 2

Mark Statkus edited this page Feb 5, 2020 · 2 revisions

SCOBot examples and code are all standalone native JavaScript. The global space in this current release creates 'scorm', 'SB' and 'SCOBotUtil'. In frameworks like Angular, global code or variables need to be accessed by passing in the '$window' argument thru the directive/controller so it can be interacted with. The SCOBot Content API libraries are function objects giving developers the option to use use the SCOBot Base (scorm), or SCOBot (SB) add-on for full automation assistance. SCOBot will self start itself when the window 'loaded' event fires. It will begin to search for the LMS Runtime API, and then once initialized will fire its own 'load' event allowing your content presentation to begin.

In an ideal world, I imagine the SCOBot portion would be a 'service' within the constraints of Angular. This is outside the scope of the project. Rather, these comments below make the appropriate options when attempting to wait on the SCOBot Content API's connection to the LMS Runtime.

With Angular, you have several options for how you render your presentation(s). But, you may choose an option that puts both SCOBot and Angular in a race. This issue is highlighted below.

Example

If you have view based HTML markup, it's expecting the controller code or you will get errors. This controller code will undoubtably finish before SCOBot has finished its initialization. This means when you go to request $window.SB.getMode(); for example, it will return '' (blank). Blank is the default mode currently within the SCOBot codebase. It gets set once the window 'loaded' event fires, it locates the LMS Runtime API (API_1484_11 for SCORM 2004 or API for SCORM 1.2). This result is the example of an out-of-order sequence.

How to deal with that in Angular JS

var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope, $window) {
    $window.SCOBotUtil.addEvent(SB, 'load', function(e) {
        alert($window.SB.getMode()); //now its available
        //Now do things with your view
        $scope.studentName = $window.SB.getvalue('cmi.learner_name');
    });
    // alternate
    $scope.init = function() {
        //this can be accessed using the DOM Element approach
        alert($window.SB.getMode()); //now its available
        //Now do things with your view
        $scope.studentName = $window.SB.getvalue('cmi.learner_name');
    };
});

An alternate approach-

//Access the controller code via the DOM Element
SCOBotUtil.addEvent('load', SB, function(e) {
    angular.element(document.getElementById('myApp')).scope().init();
});

Sample HTML View

<div id="myApp" data-ng-app="myApp" ng-app="myApp" ng-controller="myCtrl">
    <!-- Some view you set up -->
    Student Name or Email: <input type="text" ng-model="studentName"><br>
    <br>
</div>

How to deal with this in Angular 2 +

I would place the files in src/assets/js.

I would then load them in the app component or where ever it makes sense.

Please note if you use a Angular Routing you may have a issue with the server where the SCO gets deployed since this typically also requires server changes and error page handling. Just a warning.

// May need to let declare window:any to get around TypeScript warnings / TSLint
window.SCOBotUtil.addEvent(window.SB, 'load', handleLoadEvent.bind(this)); // maintain scope

When building you probably will want to also maintain a ./ relative path. ng build --prod --base-href=./.

General Warnings

You may be supporting bookmarking, so you'd would actually need to know what page to render. Ultimately, you could be simply re-rendering a game or activity with no paging. So either way, it requires the SCORM portion to get initialized before you can base your view off data contained within a SCORM Model. Its possible you could cope with this multi-page setup better with routes within Angular. It really just depends on the architecture you're using or how complicated your "app" is.

Disclaimer

This is mainly assistance information. By no means is this the way, or the only way to do something within Angular. Just a couple opinions based on observation.