Lingara Lingara Documentatie Gidsen API Bibliotheken Apps Bouwen Insluiten Webapp
Taal: Nederlands

Websitewidget

Deze pagina is vertaald uit het Engels. Als de twee verschillen, is de Engelse pagina juist. Lees de Engelse pagina

Op een website draait Lingara als custom element, <lingara-embed>. Het element tekent de oefeningen van Lingara in een frame dat Lingara serveert, zodat je pagina de gegevens van de leerling nooit ziet en de pagina van Lingara nooit de jouwe.

Het element plaatsen

Installeer @lingara/embed via npm, of laad de modulebuild vanaf een CDN als je site geen bundler heeft. Zet daarna <lingara-embed> op de pagina en definieer het. Het element accepteert:

  • plan-id, het lesplan om te oefenen, een plan dat van de speler is;
  • surface, wat er getoond wordt: practice, de standaard en het enige oppervlak dat er is;
  • theme, een van light, dark of auto;
  • locale, de taal van de eigen woorden van het frame, die anders de leerling volgt;
  • title, de toegankelijke naam van het frame.

De styling houdt op bij het thema. Je pagina kan niet in het frame reiken, en het thema is de enige instelling die het biedt.

Het tokens geven

Stel de tokenProvider van het element in op een functie die je eigen server om een token vraagt. Je server geeft het uit met de origin van de pagina, met een serverkit, en antwoordt met het token en de vervaltijd. Het element geeft het token via een bericht aan het frame, en het reist nooit in een URL.

Registreer elke origin waarop de widget draait voordat je er een token voor uitgeeft, in de instellingen van je client in de console. De pagina Serverkits legt uit hoe. De API weigert een token dat voor een andere origin is uitgegeven, en het frame toont dan een fout in plaats van de oefening.

Een serverkit kan ook het hele fragment voor je schrijven, element en tokenprovider samen, met de script-nonce van je pagina als je content security policy er een vereist. Het laatste voorbeeld hieronder laat het zien.

Luisteren naar wat het frame zegt

Het frame praat met het element via een geversioneerd berichtenprotocol, en het element maakt van wat je pagina nodig heeft DOM-events:

  • lingara:practice-completed wanneer de leerling een set afrondt, met de planId, setNumber, correct en total;
  • lingara:error wanneer het frame stopt, met een code en retryable. Een herhaalbare fout betekent dat een nieuwe plaatsing kan slagen; wanneer je het opnieuw probeert, bepaal je zelf.

Het resultaat van de oefening bereikt ook je server, zoals beschreven in oefenresultaten.

npm install @lingara/embed@0.1.0-alpha.6
# No bundler? One module script instead:
# <script type="module" src="https://cdn.jsdelivr.net/npm/@lingara/embed@0.1.0-alpha.6/dist/lingara-embed.min.js"></script>
<!-- plan-id is a lesson plan your server made for this player. -->
<lingara-embed surface="practice" plan-id="0b9d4f2e-3c1a-4e8b-9f6d-2a7c5e1b8d40" theme="auto" title="Practice"></lingara-embed>
<script type="module">
  import { defineLingaraEmbed } from '@lingara/embed'
  defineLingaraEmbed()
</script>
// Your own endpoint, on your own server. It mints with this page's origin and
// returns exactly { token, expires_at }: the token (lgr_et_…) never touches a URL.
widget.tokenProvider = async () => {
  const response = await fetch('/lingara/token', { method: 'POST', credentials: 'same-origin' })
  if (!response.ok) throw new Error(`token endpoint answered ${response.status}`)
  return response.json()
}
widget.addEventListener('lingara:practice-completed', (event) => {
  const { planId, setNumber, correct, total } = (event as CustomEvent).detail
  console.log(`set ${setNumber} of ${planId}: ${correct}/${total}`)
})
widget.addEventListener('lingara:error', (event) => {
  const { code, retryable } = (event as CustomEvent).detail
  // `retryable` means a remount may succeed; when to try is yours to decide.
  if (retryable) setTimeout(() => widget.reload(), 30_000)
  else console.warn('Lingara practice stopped:', code)
})
Taal van het codevoorbeeld

TypeScript

// The page that mounts the widget: the token never appears in it.
export function practicePage(planId: string, cspNonce: string): string {
  const widget = widgetSnippet({ planId, tokenEndpoint: '/lingara/token', theme: 'auto', locale: 'fr', title: 'Pratique', nonce: cspNonce })
  return `<!doctype html><title>Practice</title>${widget}`
}

Rust

// The element, its script build and a token provider that POSTs to your
// token route. The token never appears in the markup.
let html = widget_snippet(&SnippetOptions {
    locale: Some("fr".into()),
    nonce: Some(csp_nonce.into()), // your page's CSP nonce, for a strict script-src
    ..SnippetOptions::new(plan_id, "/lingara/token")
})?;

Go

// The plan your server made for this player, and the token route above.
// Pass the request's CSP nonce when your script-src allows no inline
// script; the token itself never appears in the markup.
theme := "auto"
html, err := lingaraembed.WidgetSnippet(lingaraembed.SnippetOptions{
	PlanID:        planID,
	TokenEndpoint: "/lingara/token",
	Theme:         &theme,
	Nonce:         &cspNonce,
})
if err != nil {
	return "", err // a *lingaraembed.SnippetError names the refused option
}
return template.HTML(html), nil // already escaped by the kit

Java

// planId is the lesson plan your server made for this player. The element
// POSTs to tokenEndpoint with same-origin cookies: the token never appears
// in the markup. Pass this request's CSP nonce if your page sets one.
String html =
    WidgetSnippet.render(
        SnippetOptions.builder(planId, "/lingara/token").locale("fr").nonce(cspNonce).build());

Kotlin

// planId is the lesson plan your server made for this player. The element
// POSTs to tokenEndpoint with same-origin cookies: the token never appears
// in the markup. Pass this request's CSP nonce if your page sets one.
val html =
    widgetSnippet(
        SnippetOptions(planId, tokenEndpoint = "/lingara/token").apply {
            locale = "fr"
            nonce = cspNonce
        },
    )

Ruby

# The plan your server made for this player, rendered into your page.
# nonce: your page's CSP nonce, when its script-src allows no inline script.
def self.widget(plan_id, csp_nonce)
  Lingara::Embed.widget_snippet(plan_id: plan_id, token_endpoint: "/lingara/token", locale: "fr", nonce: csp_nonce)
end

PHP

use Lingara\Embed\SnippetOptions;
use Lingara\Embed\WidgetSnippet;

// In your page's template: the plan your server made for this player, and
// your token route. Pass the request's CSP nonce if your script-src allows
// no inline script. A bad option throws SnippetException naming its field.
function practiceWidget(string $planId, string $cspNonce): string
{
    return WidgetSnippet::render(new SnippetOptions(
        planId: $planId,
        tokenEndpoint: '/lingara/token',
        theme: 'auto',
        nonce: $cspNonce,
    ));
}

C#

app.MapGet("/practice/{planId}", (string planId, HttpContext http) =>
{
    var widget = WidgetSnippet.Render(new SnippetOptions
    {
        PlanId = planId,                              // the plan your server made for this player
        TokenEndpoint = "/lingara/token",             // same origin: the element sends this page's cookies
        Nonce = http.Items["csp-nonce"] as string,     // your CSP's nonce, if script-src has one
    });
    return Results.Content($"<!doctype html><title>Practice</title>\n{widget}", "text/html");
}).RequireAuthorization();