How to Collect Text Responses with xAPI and Storyline

In this tutorial, you’ll learn how to capture a user’s response to an open-ended question in your Storyline course using xAPI.

Before continuing, ensure that you’ve completed the 3-part Getting Started with xAPI Tutorial Series. We will build upon what you’ve learned in those tutorials, so it’s important that you’re familiar with the basics.

This ability of xAPI to capture text entry data is one of its strengths, and if you were able to complete the Getting Started series successfully, then this should be fairly easy to implement.

Let’s get started!

Setting up your Storyline File

First, let’s make sure that your Storyline file is set up appropriately. You can return to Part 2 of the Getting Started series for in-depth instructions, but, in short, you should do the following:

  1. Add a text entry field to the slide
  2. Add your question and a submit button to the slide
  3. Style the slide as desired
  4. Change the variable name from “TextEntry” to something that better describes the answer to the question

For my question, “What did you do to prepare for this exam?” I changed the variable name to userPrepResponse.

Collect Response with xAPI Example Screenshot

Once you have your Storyline slide set up and associated text response variable renamed, you’re ready to move on to the next step.

Adding the Result and Response Objects

Now it’s time to dive back into the code. Let’s open up the xapi-statement.js file that we created during the Getting Started tutorial series. (If you’ve already completed the Collect Activity Info with xAPI tutorial, then you can build on the code you wrote for that).

We’re going to add a Result object to the statement. This optional result object can hold plenty of measurable information about the event you’re recording, such as whether or not it was a success, what the current success rate is, and whether or not the event is complete.

However, the result property that we’re most concerned is the response object. This is where we will hold the user’s response to our open-ended question. So, let’s adjust our statement accordingly.

Within the send_statement function, add a Result object after the Object object. This new Result object should take one key:value pair; the key should be “response”, and, for now, just enter a placeholder for the value. The code you’re adding should look like this:

"result": {
  "response": "This is a placeholder!"
}

In the context of the overall function, your code should look like this:

function send_statement(verb, verbId, object, objectId) {
  const player = GetPlayer();
  const uNamejs = player.GetVar("uName");
  const uEmailjs = player.GetVar("uEmail");
  const conf = {
    "endpoint": "https://trial-lrs.yetanalytics.io/xapi/",
    "auth": "Basic " + toBase64("1212:3434")
  };
  ADL.XAPIWrapper.changeConfig(conf); 
  const statement = {
    "actor": {
      "name": uNamejs,
      "mbox": "mailto:" + uEmailjs 
    },
    "verb": {
      "id": verbId,
      "display": { "en-US": verb }
    },
    "object": {
      "id": objectId,
      "definition": {
        "name": { "en-US": object }
      }, 
      "result": {
        "response": "This is a placeholder!"
      }
    }	
  };
  const result = ADL.XAPIWrapper.sendStatement(statement);
}

Important note: Don’t forget to add a comma after the closing bracket of the Object object…right before you define the Result object.

Now that the structure of our Result object is in place, we need to deal with the placeholder. We’ll do that next!

Passing the Storyline Variable to the xAPI Statement

As it stands, our user’s response gets stored in a Storyline variable once their focus leaves the text entry box. We need to pull that variable into our xAPI statement and place its contents where our placeholder is.

If you remember doing this with the user’s name and email in Part 2 of the Getting Started series, then you’re on the right track!

Let’s create a new JavaScript variable and set its contents to our Storyline variable:

const userResponse = player.GetVar("userPrepResponse");

Naturally, you would replace userPrepResponse with whatever you decided to name your variable in Storyline.

With this new JavaScript variable in place, we can replace our placeholder with it, like so:

"result": {
  "response": userResponse
}

However, we have a slight issue. What if there’s another open text response question on the following slide and we want to re-use our send_statement function? As it currently stands, that would not be possible.

If you’re thinking of parameters, then you’re exactly right. Just as we pass in the verb, verbId, object, and objectId to our function as arguments, we can pass in our Storyline variable’s name as an argument.

First, let’s add a parameter to our function that can account for the Storyline variable:

function send_statement(verb, verbId, object, objectId, openTextVar) {
  ...
}

With this in place, we can replace our Storyline variable’s name with the variable that we just added as a parameter:

const userResponse = player.GetVar(openTextVar);

The Result object can stay the same since the userResponse JavaScript variable now grabs whatever Storyline variable we tell it to.

Our final send_statement function should look like this:

function send_statement(verb, verbId, object, objectId, openTextVar) {
  const player = GetPlayer();
  const uNamejs = player.GetVar("uName");
  const uEmailjs = player.GetVar("uEmail");
  const userResponse = player.GetVar(openTextVar);
  const conf = {
    "endpoint": "https://trial-lrs.yetanalytics.io/xapi/",
    "auth": "Basic " + toBase64("1212:3434")
  };
  ADL.XAPIWrapper.changeConfig(conf); 
  const statement = {
    "actor": {
      "name": uNamejs,
      "mbox": "mailto:" + uEmailjs 
    },
    "verb": {
      "id": verbId,
      "display": { "en-US": verb }
    },
    "object": {
      "id": objectId,
      "definition": {
        "name": { "en-US": object }
      }
    }, 
    "result": {
      "response": userResponse
    }	
  };
  const result = ADL.XAPIWrapper.sendStatement(statement);
};

We can now call this function using a Storyline “Execute JavaScript” trigger whenever the user clicks the Submit button. We pass our arguments into the function, like so:

send_statement("answered", "http://adlnet.gov/expapi/verbs/answered", "Exam Prep Question", "https://www.devlinpeck.com/exam-prep-question", "userPrepResponse");

Collect Text Response with xAPI in Storyline Screenshot 2

If you’ve completed the Collect Activity Info with xAPI tutorial, then your code will look slightly different:

function send_statement(verb, verbId, object, objectId, objectDescription, activityType, openTextVar) {
  const player = GetPlayer();
  const uNamejs = player.GetVar("uName");
  const uEmailjs = player.GetVar("uEmail");
  const userResponse = player.GetVar(openTextVar);
  const conf = {
    "endpoint": "https://trial-lrs.yetanalytics.io/xapi/",
    "auth": "Basic " + toBase64("1212:3434")
  };
  ADL.XAPIWrapper.changeConfig(conf); 
  const statement = {
    "actor": {
      "name": uNamejs,
      "mbox": "mailto:" + uEmailjs 
    },
    "verb": {
      "id": verbId,
      "display": { "en-US": verb }
    },
    "object": {
      "id": objectId,
      "definition": {
        "name": { "en-US": object },
        "description": { "en-US": objectDescription },
        "type": activityType
      },
      "objectType": "Activity"
    }, 
    "result": {
      "response": userResponse
    }	
  };
  const result = ADL.XAPIWrapper.sendStatement(statement);
};

If you’re building from the previous tutorial, you will also need to pass all seven arguments to your function when you call it with your Execute JavaScript trigger in Storyline.

Publishing Your Course

And there we have it! After you publish the course and modify the output folder accordingly, the slide will send xAPI statements with the user’s response whenever they click the submit button.

You will also need to ensure that you’re collecting the user’s name and email address from the course if you want the code to work as shown.

If you’d rather use a filler name and email address to test your code, you can replace those variables with strings, like so:

const uNamejs = "testname";
const uEmailjs = "testemail@test.com";

Finally, if you haven’t configured your conf object to work with your LRS, then your statements will not send successfully. Return to Part 3 of the Getting Started series if you need additional help with this.

Conclusion

Once you’re able to use xAPI to capture user’s open responses as we’ve done in this tutorial, you are able to collect valuable information in a way that’s very user-friendly for your learners.

Without this method, you may rely on sending surveys using survey tools or bringing users out of the eLearning experience into a survey hosted on the LMS.

If you had any issues sending your statements, then you should refer to the common xAPI and Storyline troubleshooting steps.

Return to the Full Guide to xAPI and Storyline.