Can't access values in a callback

Hi, I have created a callback to check the inputs of a form in Liberty Create. I have created references for all the fields that are needed (‘wq01’ - ‘wq24’) Buy when I try to get the data, nothing is displayed,

return {
  main: function (myObj) {
    var values = myObj.get_values();
	if (!values) return;

    let score = 0;
	// Test if the values contains the fields referenced.
	let myTemp = cs.ref('wq01');
	cs.notify(values[myTemp]);

	let quiz = {
		wq01:'Avoid cliches and jargon',
		wq02:'"We design our website to be easy to use","You can find the information you need on our web site"',
		wq03:'All of the above',
		wq04:'All of the above',
		wq05:"Text that is easy to read and avoids directive text such as 'the image to the right'",
		wq06:'The most important content must be at the top of the page',
		wq07:'All of the above',
		wq08:'All of the above',
		wq09:'All of the above',
		wq10:'Activities and information about Brereton Heath Local Nature Reserve',
		wq11:'To emphasise key points only',
		wq12:'To emphasise key points only',
		wq13:'Tabular information only',
		wq14:'One heading 1 at the top of the page',
		wq15:'Waste and Recycling in Cheshire East',
		wq16:'The British Regulator for Educational Welfare (BREW) was established in 2011',
		wq17:'Option 1',
		wq18:'Include (external link) in the link title',
		wq19:'No',
		wq20:'More than just white (empty) space. For example a bulleted list',
		wq21:'All of the above',
		wq22:"Only if they will enhance the reader's understanding of the subject",
		wq23:'Yes',
		wq24:'Documents must be created in a structured accessible way',
	};

	Object.entries(quiz).forEach(([k,v]) => {
		if (v === values[cs.ref(k)]) {
			score += 1;
		}
	});
    myObj.set_value(score);
  },
};

I still haven’t managed to get this working. Any help would be appreciated!

What are you seeing returned from get_values()? Could you log it out and paste here?

1 Like

Hi Bob, thanks for your reply.
I get this:

Array
(
    [:CMP0000122GBHRW1] => 0
    [:id] => 11429
)

If I put a question on the last page (where the “Score” bit that uses the callback sits), I get some output:

* 19:05:21.2996 ** My console log **
* 19:05:21.2996 array([REL0000514GBHRW1:PRO0001970GBHRW1] => "DTC0001689GBHRW1", [:CMP0000122GBHRW1] => "0", [:id] => "11491")
* 19:05:21.3006 This should be something: DTC0001689GBHRW1

Ok so yes that looks correct, the question must be a Choice field, so you get back the ID of the selected choice (‘DTC0001689GBHRW1’).

This will complicate your code a little as you have hard coded the values (not IDs) for comaprison, so you’d need to convert the choice ID to value first.

Probably the best option is instead to create a Reference to each of the correct Choice answers, and use those in your code, instead of hardcoded answers. That also avoids any problems in future with the text of the answer ever being changed or reworded, which would break your current code. Additionally you could later alter the ‘correct’ answer for a question just by changing the Reference to point to a different Choice, no need for a code change.

So, imagining your answer references are named ‘wa01’ etc, your pseudo code may look like this:

...
	let quiz = {
		wq01: 'wa01',
		wq02: 'wa02',
		...
	};

	Object.entries(quiz).forEach(([k,v]) => {
		if (cs.ref(v) === values[cs.ref(k)]) {
			score += 1;
		}
	});

	myObj.set_value(score);
1 Like

Hi Bob,

Thanks, that makes sense. But am I able to get these references to work if the fields / questions are on a different page of the form? We are trying to do one question per page to be like GovUK GDS.

Hi Bob, I realise I was setting up my forms wrong. I think I see how to get it to work now.

Hi Bob, I am still having a problem getting the question references. Here’s my code:

return {
  main: function (myObj) {
    var values = myObj.get_values();
	if (!values) return;
	let score = 0;
	console.log(`First question: ${cs.ref("wq01")}`);
	console.log(`First answer: ${cs.ref("wa01")}`);
	console.log(values);
	let quiz = Object.fromEntries(Array.from({length: 24}, (_, i) => {
		let num = (i+1).toString().padStart(2,'0');
		return [`wq${num}`, `wa${num}`];
	}));

	Object.entries(quiz).forEach(([k,v]) => {
		let ans;
		switch(v) {
			case "wa02":
			ans = '"We design our website to be easy to use","You can find the information you need on our web site"';
			break;
			case "wa18":
			ans = 'No';
			break;
			case "wa23": 
			ans = 'Yes';
			break;
			default:
			ans = cs.ref(v);
		}
		if (ans === values[cs.ref(k)]) {
			score += 1;
		}
	});
    myObj.set_value(score);
  },
};

And this is the output:

* 11:30:02.7517 First question: REL0000514GBHRW1:PRO0001946GBHRW1
* 11:30:02.7519 First answer: DTC0001603GBHRW1
* 11:30:02.7519 array([REL0000514GBHRW1:PRO0002018GBHRW1] => , [:id] => "11835")

When I had the question on the same page, I was getting the id for the question, but now nothing is coming through.

I’m not quite following what you mean about having the question on the same page?

get_values() will only return the form fields which are in the same widget group as the callback.

1 Like

Thanks Bob.

Maybe I am trying to do this the wrong way. It’s supposed to be a quiz with a series of questions. On the last page of the form, I want to be able to display the score.

Is there a different design pattern I should use for this kind of thing?

Many thanks for your help Bob, I’ve had some help on a mentoring session which has helped me resolve this. I added all the fields in a hidden row in an information widget on the last page.

Great, glad you are sorted.

Now I understand your scenario better, a callback is probably not the ideal solution as this is aimed at scenarios where you need it to update live as the form values are changed. But it will work as you described.

If you are collecting answers across multiple pages and saving them, and then want to show a score at the end there are a number of other ways of doing this. A Code Studio presenter or widget which read the saved values and work out the score would be one, or an Event Action could be used to calculate the score and store it in a Property at the end of form submission, so you’re not just calulating it on the fly and can use it elsewhere later.

If this is a one off quiz then those will all probably suffice, but if you intend to have multiple quizzes in future then there are better approaches. Building the questions and quizzes as records would allow for proper administration of them, and without needing to hard code any answers in Code Studio or References.

There will be other users on the forum who have built quiz and survey systems like this in the past, so if you need that more high level approach advice in future then just post another question.

1 Like