Implementing with VueJS - help needed

913 views
Skip to first unread message

Miguel Real

unread,
Jun 13, 2021, 1:16:16 PM6/13/21
to eLearning Technology and Development
Hello all.

I'm trying to implement a tool in VueJS that can load a course in a iframe and communicate with my backend API made with nodeJS.

In VueJS code i'm implementing in the template area an iframe where i load the course url launch html page.

<template>
(...)
                    <iframe
                        :src="courseURL"
                        name="course"
                        frameborder="0"
                    ></iframe>
(...)
</template>



In javascript part i'm importing the pipwerks-scorm-api-wrapper from https://github.com/allanhortle/pipwerks-scorm-api-wrapper like the documentation describes:

import { SCORM } from 'pipwerks-scorm-api-wrapper'



On the created hook i make the window API object like this:
this.window.API = {
                cmi: {
                    suspend_data: '',
                    launch_data: '',
                    comments: '',
                    comments_from_lms: '',
                    core: {
                        student_id: '@jcputney',
                        student_name: 'Jonathan Putney',
                        lesson_location: '0',
                        credit: 'credit',
                        lesson_status: 'incomplete',
                        entry: 'ab-initio',
                        lesson_mode: 'normal',
                        exit: '',
                        session_time: '00:00:00',
                        score: {
                            raw: '',
                            min: '',
                            max: '100'
                        },
                        total_time: '00:00:00'
                    },
                    objectives: {},
                    student_data: {
                        mastery_score: '',
                        max_time_allowed: '',
                        time_limit_action: ''
                    },
                    student_preference: {
                        audio: '',
                        language: '',
                        speed: '',
                        text: ''
                    },
                    interactions: {}
                }
            }
            this.window.API.LMSInitialize = SCORM.init
            this.window.API.LMSGetValue = SCORM.get
            this.window.API.LMSSetValue = SCORM.set
            this.window.API.LMSCommit = SCORM.save
            this.window.API.LMSFinish = SCORM.quit
            this.window.API.LMSGetLastError = SCORM.debug.getCode
            this.window.API.LMSGetDiagnostic = SCORM.debug.getDiagnosticInfo
            this.window.API.LMSGetErrorString = SCORM.debug.getInfo

I uypdate the source iframe html file to load scorm content:
this.courseURL = '/assets/courses/scorm_examples/iseazy_scorm/index.html'

Then i try to init with code:
this.lmsConnected = SCORM.init()

The results are a blank screen and in browser develop console i get a loop with the message:
connection.initialize called.


What i'm missing or doing wrong? Anyone have a clue and can help me?

Best regards,
Miguel.

Philip Hutchison

unread,
Jun 15, 2021, 12:29:31 PM6/15/21
to elearning-technolo...@googlegroups.com
Hi Miguel

This code will not work:

            this.window.API.LMSInitialize = SCORM.init
            this.window.API.LMSGetValue = SCORM.get
            this.window.API.LMSSetValue = SCORM.set
            this.window.API.LMSCommit = SCORM.save
            this.window.API.LMSFinish = SCORM.quit
            this.window.API.LMSGetLastError = SCORM.debug.getCode
            this.window.API.LMSGetDiagnostic = SCORM.debug.getDiagnosticInfo
            this.window.API.LMSGetErrorString = SCORM.debug.getInfo

SCORM.init from the wrapper invokes the API's LMSInititalize method, it is not a reference to it. So here, instead of invoking API.LMSInitialize, you're redefining it to be the same function as SCORM.init. Same for all of the other API methods.

What you need to do is properly define the API methods to perform the tasks they were meant to perform. For example, LMSGetValue is meant to look up the passed parameter (e.g. LMSgetValue("cmi.suspend_data")) from the LMS's data store and return it as a string. If the item doesn't have a value, the function needs to return an empty string. (This will vary from method to method, check the SCORM docs for the proper behavior and error codes for each API method.)

Defining these functions to fully comply with SCORM standards will take quite some work. You may want to look at the SCORM API code found in open-source solutions like Moodle for a jump-start or examples.

Good luck
- philip



--
You received this message because you are subscribed to the Google Groups "eLearning Technology and Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elearning-technology-and...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elearning-technology-and-development/7dd09d5f-e580-4647-9256-59974d3cf0fdn%40googlegroups.com.

Miguel Real

unread,
Jun 16, 2021, 7:01:07 PM6/16/21
to eLearning Technology and Development
Hi all.

Before i post my doubts i already did try to remove that statements redifining the API methods. I already have some functions declared on VueJS methods hook but with no luck. In console i get the error 

connection.initialize called.
SCORM.API.find: API found. Version: 1.2
API: [object Object]

vue.runtime.esm.js?dce0:619 [Vue warn]: Error in created hook (Promise/async): "TypeError: API.LMSInitialize is not a function"
found in
---> <CourseLearn> at src/views/courses/Learn.vue
       <QPageContainer>
         <Main> at src/layout/Main.vue
           <QLayout>
             <App> at src/App.vue
               <Root>

TypeError: API.LMSInitialize is not a function
    at Object.pipwerks.SCORM.connection.initialize [as init] (index.js?4d47:310)
    at _callee$ (Learn.vue?1b24:426)
    at tryCatch (runtime.js?750b:63)
    at Generator.invoke [as _invoke] (runtime.js?750b:293)
    at Generator.eval [as next] (runtime.js?750b:118)
    at asyncGeneratorStep (asyncToGenerator.js?1da1:3)
    at _next (asyncToGenerator.js?1da1:25)
    at eval (asyncToGenerator.js?1da1:32)
    at new Promise (<anonymous>)
    at eval (asyncToGenerator.js?1da1:21)

All of this functions are declared in my methods hook:
        LMSInitialize (parameter) {
            console.log('LMSInitialize =>', parameter)
            return 'true'
        },

        LMSGetValue (varname) {
            console.log('LMSGetValue ==>', varname, 'value =>', this.window.API[varname])
            return this.window.API[varname]
        },

        LMSSetValue (varname, varvalue) {
            console.log('LMSSetValue => ', varname, varvalue, 'current value =>', this.window.API[varname])
            this.window.API[varname] = varvalue
            console.log('LMSSetValue => ', varname, varvalue, 'new value =>', this.window.API[varname])
            return 'true'
        },

        LMSCommit (parameter) {
            console.log('LMSCommit =>', parameter)
            this.LMSGetValue('')
            return 'true'
        },

        LMSFinish (parameter) {
            console.log('LMSFinish =>', parameter)
            return 'true'
        },

        LMSGetLastError (error) {
            console.log('LMSGetLastError ==> ', error)
            return 0
        },

        LMSGetDiagnostic (errorCode) {
            console.log('LMSGetDiagnostic =>', errorCode)
            return 'diagnostic string => ' + errorCode
        },

        LMSGetErrorString (errorstring) {
            console.log('LMSGetErrorString ==>', errorstring)
            return 'error string => ' + errorstring
        },

So, can anybody help me to set this up? It seems that i'm not doing something in the right way but i didn't discover yet what it is... any help will be appreciated!

Best regards,
Miguel.

Ryan Meyer

unread,
Jun 16, 2021, 11:06:44 PM6/16/21
to elearning-technolo...@googlegroups.com
Are you defining those LMSInitialize, LMSGetValue, etc functions as methods of your API object?

I can see in the console output that the SCORM code is *finding* the object named API, but it’s not able to find the LMSInitialize method on it.

Maybe setup a breakpoint in your debugger and trace into the SCORM init call, to see what properties and methods are being exposed on the API object you’re creating.

-Ryan

On Jun 16, 2021, at 7:01 PM, Miguel Real <migue...@gmail.com> wrote:

Hi all.

Miguel Real

unread,
Jun 17, 2021, 5:26:56 AM6/17/21
to eLearning Technology and Development
Hello.

Yesterday night i made some progress. I defined the scorm functions like Ryan is saying in previous post. Examples:
        window.API.LMSInitialize = function (parameter) {
            return 'true'
        }

        window.API.LMSGetValue = function (varname) {
            let value = SCORM.get(varname)
            console.log('LMSGetValue ==>', varname, value)
            return value
        }

However SCORM.get is not able to retrieve the value of varname parameter. I made my own functions to retrieve the value of window.API.cmi object keys and they are retrived. Any clue for this behaviour?

It seems in this way that i'm not using at all the pipwerks-scorm-api-wrapper methods, or at least, they will not be used.



Best regards,
Miguel.

Ryan Meyer

unread,
Jun 17, 2021, 7:56:26 AM6/17/21
to elearning-technolo...@googlegroups.com
Miguel,

I think you’re confused about the intent of the SCORM wrapper. It’s intended to make it easier to interact with the SCORM API object (the one you’re building in window.API). That SCORM window.API object is always provided by the LMS itself. And because there are different versions of SCORM with different object and function names, and it can sometimes be located in various places in the parent window hierarchy (not always just in window.API), the SCORM wrapper helps solve some of those issues.

In your case, you’re apparently implementing your own LMS, so you’re providing the API object as well. Just remember, the API object shouldn’t know anything about the SCORM wrapper or your course.

The order of calls should be:
Your course -> SCORM wrapper -> API -> server

Hope that’s helpful,
Ryan

On Jun 17, 2021, at 5:26 AM, Miguel Real <migue...@gmail.com> wrote:

Hello.

Philip Hutchison

unread,
Jun 17, 2021, 12:01:05 PM6/17/21
to elearning-technolo...@googlegroups.com
Echoing Ryan's comment:

The SCORM workflow essentially has two pieces:

1. The learning management system, which receives data requests from a course (set/get) via a JavaScript API that the server must provide
2. The course, which needs to be able to communicate with the LMS via the JavaScript API

The pipwerks wrapper is meant to be used by the course. It simplifies the SCORM syntax on the client side and handles some of the tedious tasks on your behalf. It requires the LMS to provide the SCORM API in the course's main window (typically a parent frame).

Miguel, in your scenario you are trying to recreate the server-side SCORM code. This has nothing to do with the pipwerks wrapper. You will need to define the window.API object, and it will need to return all of the expected values, in the expected formats, or the pipwerks wrapper (and any other SCORM wrappers) will throw an error. Check out the official SCORM docs or scorm.com's handy reference chart to better understand what each of the API methods should be returning https://scorm.com/scorm-explained/technical-scorm/run-time/run-time-reference/#section-2

Good luck
- philip



Miguel Real

unread,
Jun 17, 2021, 1:18:55 PM6/17/21
to eLearning Technology and Development
Hi.

At first i want to thank you all for your inputs.

I tryed before your approach and tried to replicate just now too. Here is what i've done:

<script>
import { SCORM } from 'pipwerks-scorm-api-wrapper'

export default {
created () {
            },
            LMSInitialize: (parameter) => 'true',
            LMSGetLastError: (parameter) => '0',
            LMSGetValue: (parameter) => '0',
            LMSCommit: (parameter) => '0',
            LMSSetValue: (parameter) => '0',
            LMSGetErrorString: (parameter) => '0'
        }

        this.courseURL = '/assets/courses/scorm_examples/iseazy_scorm/index.html'
        this.lmsConnected = SCORM.init()

        console.log('lmsConnected => ', this.lmsConnected)
}
</script>

This is the result for now:
screenshot_console.png

So, for what i understand, in those API functions i should provide communication betwwen my backend to get and set values, right? Those values will be returned by that window.API methods to the scorm wrapper that will be responsible to update the window.API object properties, or am i missing something?

And how about this wrapper calls:
        SCORM.save()
        SCORM.quit()

When to use them? How to trigger them? when the user closes window or change to other application screen/route? What is your advice?

Best regards,
Miguel.

Philip Hutchison

unread,
Jun 17, 2021, 1:35:28 PM6/17/21
to elearning-technolo...@googlegroups.com
Yes, the window.API methods are meant to get/set values from your backend. In most LMSs, when a course launches, a parent window is created, containing the window.API object. (The course is typically in a child frame.) This window.API object is populated at creation with all relevant course data. The APi is then used as a form of client-side state management, storing values locally in the window.API JS object. LMSs will typically send the data to the database when one of two triggers occur: when a user exits the course (clicks exit or simply closes the window), or when the course performs a LMSFinish() call (scorm.set() if you're using the pipwerks wrapper). These triggers tell the LMS to 'persist' the data -- to send the data to the database. As the window.API/LMS developer, you must decide how you want to persist the data on your backend. Most LMSs use AJAX to push the window.API object to the server, where it gets parsed and stored as needed.

If you look at the wrapper code, which is actually quite short, save() invokes LMSCommit() and quit() invokes LMSFinish(). Your APi will need to support those calls. As for what those LMS functions do, please refer to the link I provided in my last email.





Ryan Meyer

unread,
Jun 17, 2021, 3:49:05 PM6/17/21
to elearning-technolo...@googlegroups.com
Part of the challenge is that you’re implementing a LOT all at once - a course that uses SCORM, and a full SCORM LMS back-end.

It may be helpful for you to start with your course (that includes the SCORM wrapper), and deploy and test it on SCORM Cloud (https://rusticisoftware.com/products/scorm-cloud/ ) That way you’ll know that your course can properly initialize, read/write info, and save completion to a standard SCORM API.

Then, you can focus on implementing your own API (if you still need to) to manage all the back-end data connections to your server. Although, you may find that SCORM Cloud suits your needs just fine, and you don’t even have to worry about implementing your own server application!

-Ryan 

On Jun 17, 2021, at 1:35 PM, Philip Hutchison <phi...@pipwerks.com> wrote:


<screenshot_console.png>

Miguel Real

unread,
Jun 17, 2021, 5:47:03 PM6/17/21
to eLearning Technology and Development
Hello again.

First of all i want to thank you for all your help. I was doing things with mistakes and i wasn't able to load and init the scorm api and wrapper in the right way.

@Ryan: Well, i do not intend to develop scorm courses, i'm just trying to load them and reproduce like a LMS system would do. I just downloaded some scorm courses packages samples and i'm trying to load and reproduce them on my application. Of course i want to do some backend communication, so an user can have proper interation with scorm courses and that interation persists along time.

I've tried already load some of these packages on scorm cloud to test them and indeed it's a fine solution but we are trying to make our own solution. For now, i'll try the challenge to improve the functionalities of our application. 

However i would like to question how is the best way to store scorm packages and serve them to the app? My backend is developed on nodeJS and my frontend in VueJS. 

Best regards,
Miguel.

Philip Hutchison

unread,
Jun 17, 2021, 6:22:24 PM6/17/21
to elearning-technolo...@googlegroups.com
SCORM packages are zip files containing standard HTML and JS files. The SCORM spec says the content should be served in a frame. So build your Vue app however you like, then serve the course pages as native HTML/JS/CSS within the iframe. 

I’ve built a Vue app that works this way, it works fine for me, but my app is designed for local access only. Be sure to consider security and access privileges… only an authenticated user should be able to see the content. 

As for SCORM support, honestly, I don’t recommend trying to build out the SCORM backend unless you know for certain that all the courses will use the bare minimum SCORM calls and a plain manifest. The API is just one piece of SCORM support. You also have to parse manifests and support all the different ways a course can be configured. There are a LOT of intricacies and details to cover when building out SCORM support. Don’t expect that you can do it quickly, it’s a huge job. Best off using a 3rd party service like SCORM Cloud, which ensures full SCORM compliance and will save you a ton of time. 



Ryan Meyer

unread,
Jun 17, 2021, 7:38:13 PM6/17/21
to elearning-technolo...@googlegroups.com
Just here to confirm everything Philip said. I’ve also led a team that built a niche SCORM back end. It made sense for the specific use case we had (still going strong several years later after 100s of thousands of users and millions of completions!) but it was highly constrained as Philip pointed out.

One other thing to mention: if you are only implementing the back end and not the courses, then Philip’s SCORM wrapper is useless to you and shouldn’t even be part of your code base. That SCORM wrapper code is intended to be part of the course, easing the calls it makes to the SCORM API. If you’re focusing on implementing the API itself, then you don’t need the SCORM wrapper. The sample courses you’ve downloaded will already be making calls directly to the window.API object you’re setting up with the LMS____ menthods.

-Ryan

On Jun 17, 2021, at 6:22 PM, Philip Hutchison <phi...@pipwerks.com> wrote:



Miguel Real

unread,
Jun 19, 2021, 11:34:47 AM6/19/21
to eLearning Technology and Development
Hi there.

Another team of my company will be responsible to make courses through a commercial tool called SAP Enable Now. Do you think that they need to embed the scorm wrapper code in their final work? If so, where it shoudl be inserted? In the html file that is resposible to launch the course?

Thanks for your support!

Best regards,
Miguel.

Philip Hutchison

unread,
Jun 19, 2021, 2:34:48 PM6/19/21
to elearning-technolo...@googlegroups.com
If the courses are being built by SAP Enable Now, you might not even need SCORM. I'd look into their documentation for integrations and try to build your Vue app based on SAP's infrastructure, which is completely unrelated to SCORM.

But if you still want to try building your own SCORM backend (which we have recommended you don't, because it's a very large and complex task with little reward), SAP Enable Now courses can be exported as SCORM packages. I have zero experience with SAP, but a quick Google turns up some hits:


Remember that the pipwerks SCORM wrapper is just a convenience utility for developers who are hand-building their own courses. If you export an SAP course as a SCORM package, the pipwerks wrapper will be irrelevant, as the SCORM code is already taken care of for you by the SAP exporter.The pipwerks wrapper has no place in your workflow.








Reply all
Reply to author
Forward
0 new messages