Client-side Components

Client-side components are implemented using Web Components. Although you can implement your Web Components anyway you like, we favor the use of Polymer which offers a lot of high level goodies, such as declarative declaration syntax and data-binding.

Compatibility checklist

The following checklist describes the minimum recommended interfaces and conventions that ASQ elements should support:

  1. Naming convention. The naming convention is to start with asq-q. If it’s a question type, finish with -q.
  2. Behaviors. implement asqElementBehavior or asqQuestionElementBehavior.
  3. Roles. Internally have a different element for each role
  4. [value property]. (question type elements only).
  5. <asq-solution> element support. (question type elements only).

Naming conventions

It’s advised that all ASQ plugins start with asq-. In addition, question type elements should end in -q.

Examples

Non-question elements:
asq-canvas, asq-welcome.

Question types:
asq-multi-choice-q, asq-highlight-q.

Behaviors

The asq-base element contains a set of base behaviors that each asq-element should implement. These behaviors add role support, uid property, whether the element is a question element and more. Depending on whether your ASQ element is a plain element or a question type element you should use asqElementBehavior or asqQuestionElementBehavior respectively.

Example

behaviorview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../asq-base/asq-base.html">

<dom-module id="asq-my-question-type-q">
<template>
<h1>This is an ASQ question type element</h1>
</template>

<script>
Polymer({
is: "asq-my-question-type-q",
behaviors: [
ASQ.asqQuestionElementBehavior
],
});
</script>
</dom-module>

Roles

Each client-side component is encapsulated in a root Custom HTML Element. It may display different UI and support different functionality depending on the role of the user. To simplify this task it’s wise to separate complex components int sub-components by role. The root custom element is responsible for rendering the appropriate role-based sub-component.

Example

Root Elementview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../asq-base/asq-base.html">
<link rel="import" href="./elements/asq-my-question-type-q-presenter.html">
<link rel="import" href="./elements/asq-my-question-type-q-viewer.html">

<dom-module id="asq-my-question-type-q">
<template>
<h1>This is an ASQ question type element</h1>

<template is="dom-if" if="{{hasRole(role, roles.PRESENTER)}}" restamp="true">
<asq-my-question-type-q-presenter></asq-my-question-type-q-presenter>
</template>

<template is="dom-if" if="{{hasRole(role, roles.VIEWER)}}" restamp="true">
<asq-my-question-type-q-viewer></asq-my-question-type-q-viewer>
</template>
</template>

<script>
Polymer({
is: "asq-my-question-type-q",
behaviors: [
ASQ.asqQuestionElementBehavior
],
});
</script>
</dom-module>

Notice the use of hasRole() which is defined in asqElementBehavior from asq-base.html and the use of restamp="true" to avoid having both role elements in the DOM. For more on restamp have a look at Polymer documentation.

Viewer Elementview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../asq-base/asq-base.html">

<dom-module id="asq-my-question-type-q-viewer">
<template>
<h2>This is the <em>viewer</em> element for &lt;asq-my-question-type-q&gt; </h2>
</template>

<script>
Polymer({
is: "asq-my-question-type-q-viewer",
behaviors: [
ASQ.asqQuestionElementBehavior
],
});
</script>
</dom-module>

Presenter Elementview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../asq-base/asq-base.html">

<dom-module id="asq-my-question-type-q-presenter">
<template>
<h2>This is the <em>presenter</em> element for &lt;asq-my-question-type-q&gt; </h2>
</template>

<script>
Polymer({
is: "asq-my-question-type-q-presenter",
behaviors: [
ASQ.asqQuestionElementBehavior
],
});
</script>
</dom-module>

value property (question type elements only)

Each question type should have a value property that represents the current answer of a viewer. To easily set the value of a question to the value of the solution, this property should be compatible with the format of the solution that is specified in the <asq-solution> element inside each question type element.

<asq-solution> element support

During authoring, in question-type elements that support a solution, the solution should be provided within one <asq-solution> element. Multiple elements will be ignored and only the first should be taken into consideration. The format of the solution is pertinent to the question type but in general should be something we can call JSON.stringify on. The <asq-solution> element should be stripped off from the server-side component during the parse-html hook. IT SHOULD NOT BE SERVED to the clients. Instead, when the solution is needed, it can be pulled from the server with the appropriate events.

1
2
3
4
5
6
7
8
<asq-my-question-type-q>
<asq-solution>
{
"solution-field-1" : "solution value for field 1",
"solution-field-2" : "solution value for field 2",
}
</asq-solution>
</asq-my-question-type-q>

Communication

PubSub

Components can listen for the asq-ready event which will provide them with an eventbus which can be used for communication with the “core” client app. Normally you will do something like this on your root component element:

1
2
3
document.addEventListener('asq-ready', function (evt) {
this.eventBus = evt.detail.asqEventBus
}.bind(this));

and then pass via data-binding the eventBus to the role-specific elements.

1
2
3
4
5
6
7
8
9
10
11
12
13
<dom-module id="asq-my-question-type-q">
<template>
<h1>This is an ASQ question type element</h1>

<template is="dom-if" if="{{hasRole(role, roles.PRESENTER)}}" restamp="true">
<asq-my-question-type-q-presenter event-bus="[[eventBus]]"></asq-my-question-type-q-presenter>
</template>

<template is="dom-if" if="{{hasRole(role, roles.VIEWER)}}" restamp="true">
<asq-my-question-type-q-viewer event-bus="[[eventBus]]"></asq-my-question-type-q-viewer>
</template>
</template>
</dom-module>

Then you can consume events as follows:

1
this.eventBus.on('asq:question_type', cb);

where cb is a callback function to call when the asq:question_type event occurs.