@arbetsformedlingen/kompetensvaljaren
v2.3.0
Published
Fristående webbkomponent för att välja kompetenser.
Downloads
324
Readme
Kompetensväljaren (digi-service-skill-selector)
Kompetensväljaren är en webbkomponent för att välja kompetenser. Hela kompetenskatalogen är sökbar, och kompetenser som hör till det valda yrket prioriteras. Den laddar en liten basfil (kv-base.v1.json) och hämtar yrkesspecifik data (kontextshards) först när den behövs.
Beskrivning
- Hitta rätt kompetens för yrket Hela kompetenskatalogen är sökbar. När ett yrke är valt, till exempel från Yrkesväljaren, prioriteras kompetenser som hör till yrket.
- Hög prestanda Sökningen sker lokalt i webbläsaren. Komponenten laddar en liten basfil och hämtar yrkesdata först när den behövs.
- Flexibel datahantering Versionerat npm-datapaket (rekommenderas), automatiskt uppdaterad data eller en egen URL.
- Headless-API Samma sökning finns som funktion för klienter som bygger ett eget gränssnitt.
Installation
Installera komponenten och datapaketet:
npm install @arbetsformedlingen/kompetensvaljaren @arbetsformedlingen/kompetensvaljaren-data@31Datapaketets majorversion är taxonomiversionen: 31.x.y innehåller taxonomi v31. Med ^31.0.0 i package.json får du rättningar av v31-datan men byter aldrig taxonomiversion av misstag.
Välj läge
Låst version(rekommenderas) Datapaketet registreras viaregisterSkills(await loadBase()).SenasteUtan registrerad data hämtar komponenten senaste datan från CDN.Egen URLAvancerat läge. Pekaaf-data-urlmot en egen hostadkv-base.v1.json.
Användning i Angular
Exemplet kopplar ihop Kompetensväljaren med Yrkesväljaren och låser datan för båda till taxonomi v31:
npm install @arbetsformedlingen/kompetensvaljaren @arbetsformedlingen/kompetensvaljaren-data@31 @arbetsformedlingen/yrkesvaljaren @arbetsformedlingen/yrkesvaljaren-data@31Registrera datan och båda komponenterna i main.ts innan appen startar:
// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { defineCustomElements as defineYv } from '@arbetsformedlingen/yrkesvaljaren/loader';
import { defineCustomElements as defineKv } from '@arbetsformedlingen/kompetensvaljaren/loader';
import { registerSkills } from '@arbetsformedlingen/kompetensvaljaren';
import { registerTaxonomy } from '@arbetsformedlingen/yrkesvaljaren';
import { loadBase } from '@arbetsformedlingen/kompetensvaljaren-data';
import { appConfig } from './app/app.config';
import { App } from './app/app';
async function main() {
// Yrkesdatan (cirka 2 MB) importeras dynamiskt så att den hamnar i en egen chunk.
const { metadata, taxonomyData } = await import('@arbetsformedlingen/yrkesvaljaren-data');
registerTaxonomy({ data: taxonomyData, metadata });
registerSkills(await loadBase({ basePath: '/assets/kv-v31/' }));
defineYv(window);
defineKv(window);
await bootstrapApplication(App, appConfig);
}
main().catch((err) => console.error(err));Kopiera KV-datan som assets med en regel i angular.json (projects.<projekt>.architect.build.options.assets):
{
"glob": "**/*",
"input": "node_modules/@arbetsformedlingen/kompetensvaljaren-data/dist",
"output": "/assets/kv-v31/"
}Koppla sedan Yrkesväljaren till Kompetensväljaren i en standalone-komponent:
// src/app/skills.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA, ElementRef, signal, viewChild } from '@angular/core';
import type { KvSelection } from '@arbetsformedlingen/kompetensvaljaren';
type SkillSelectorElement = HTMLElement & { setOccupationSelection(selection: unknown): Promise<string[]> };
@Component({
selector: 'app-skills',
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<digi-service-job-selector af-label="Välj yrke" (afJobSelected)="onJobSelected($event)"></digi-service-job-selector>
<digi-service-skill-selector #kv af-label="Välj dina kompetenser" (afKompetensSelection)="onSkills($event)"></digi-service-skill-selector>
<p>Valda kompetenser: {{ skills().length }}</p>
`,
})
export class SkillsComponent {
private readonly kv = viewChild.required<ElementRef<SkillSelectorElement>>('kv');
readonly skills = signal<KvSelection[]>([]);
onJobSelected(event: Event): void {
void this.kv().nativeElement.setOccupationSelection((event as CustomEvent).detail);
}
onSkills(event: Event): void {
this.skills.set((event as CustomEvent<KvSelection[] | null>).detail ?? []);
}
}Bygger ni ett eget gränssnitt i stället för webbkomponenten, använd headless-sökningen. Ett komplett Angular-exempel finns i examples/angular-multi-occupation-skills.
Användning
Låst version
Registrera datapaketets base-data innan komponenten renderas. Komponenten ska inte mountas förrän registerSkills(...) är klar.
<script type="module">
import { defineCustomElements as defineSkillSelector } from "@arbetsformedlingen/kompetensvaljaren/loader";
import { registerSkills } from "@arbetsformedlingen/kompetensvaljaren";
import { loadBase } from "@arbetsformedlingen/kompetensvaljaren-data";
defineSkillSelector(window);
registerSkills(await loadBase());
</script>
<digi-service-skill-selector
af-label="Välj dina kompetenser"
af-occupation-id="GPNi_fJR_B2B"
></digi-service-skill-selector>I vissa devservrar och bundlers behöver datapaketets JSON exponeras som en publik asset. Om filen ligger på en annan sökväg än datapaketets bundle kan loadBase() få en explicit URL:
registerSkills(await loadBase({ basePath: "https://d3hng02m5bee8m.cloudfront.net/kompetensvaljaren/latest/" }));Det hämtar kv-base.v1.json från den angivna basePath-URL:en. Kontextshards hämtas relativt samma basefil. När bundlern hanterar paketassets korrekt räcker registerSkills(await loadBase()).
Senaste från CDN
Om ni inte registrerar någon data manuellt försöker komponenten först ladda af-data-url. Om ingen URL anges laddar komponenten Senaste. Om en explicit URL anges och den inte kan laddas går komponenten till felläge i stället för att tyst byta dataset.
<digi-service-skill-selector
af-label="Välj dina kompetenser"
></digi-service-skill-selector>CloudFront-URL för Senaste är:
https://d3hng02m5bee8m.cloudfront.net/kompetensvaljaren/latest/kv-base.v1.jsonShards ska ligga relativt till denna:
https://d3hng02m5bee8m.cloudfront.net/kompetensvaljaren/latest/shards/kv-context-222.v1.jsonDirekt från Yrkesväljaren
Kompetensväljaren kan ta Yrkesväljarens afJobSelected.detail direkt:
const yv = document.querySelector('digi-service-job-selector');
const kv = document.querySelector('digi-service-skill-selector');
yv.addEventListener('afJobSelected', (event) => {
kv.setOccupationSelection(event.detail);
});occupation-name använder sitt eget Concept ID. job-title använder sitt kopplade related occupation-name. Vid flera val blir det första mappningsbara yrket primär kontext och resterande unika yrken extra kontext. Noll val ([]) eller inga mappningsbara val rensar tidigare yrkeskontext.
Lågnivå-API:t af-occupation-id/af-input-ids finns kvar för konsumenter som redan har taxonomi-ID:n. KV:s datapaket fortsätter att vara byggt kring occupation-name och ssyk-level-4; jobbtitlar läggs inte in i KV-data.
Mer detaljer finns i guiden för YV → KV.
Vite
Kompetensväljaren använder en Stencil-loader som hämtar komponenternas entry-filer dynamiskt. I Vite ska loader-paketet därför undantas från dependency optimizer, annars kan Vite flytta loadern till .vite/deps och de dynamiska importerna får fel sökväg.
// vite.config.js
export default {
optimizeDeps: {
exclude: [
'@arbetsformedlingen/kompetensvaljaren/loader',
],
},
};Designsystem-kompatibilitet
Kompetensväljaren är uppbyggd med designsystemets tokens, typografi, färger, fokusramar och ikoner och kommer att uppdateras automatiskt för dig som har designsystemet installerat. Den använder de förväntade attributen för att styla etikett, felmeddelande osv, men har inga komponentberoenden till designsystemet. För den som inte har designsystemet installerat används inbyggda fallbacks för att bibehålla utseendet.
Avancerat: Egen URL
Om du inte registrerar data manuellt kan komponenten i stället ladda kv-base.v1.json via af-data-url. Detta är ett avancerat läge för team som själva vill styra hosting och caching. Om den URL:en inte kan laddas går komponenten till felläge i stället för att tyst byta dataset.
https://d3hng02m5bee8m.cloudfront.net/kompetensvaljaren/latest/kv-base.v1.jsonPublikt API
| Property | Attribut | Typ | Standard | Beskrivning |
| --- | --- | --- | --- | --- |
| afOccupationId | af-occupation-id | string | '' | Primär kontext. Kan vara ett Concept ID för occupation-name eller ssyk-level-4. |
| afInputIds | af-input-ids | string \| string[] | [] | Extra kontext-id:n. Strängvärden kan vara kommaseparerade eller en JSON-array. Implementationens parser begränsar inte antalet id:n. |
| label | af-label | string | 'Sök eller välj dina kompetenser' | Etikett för fältet. |
| placeholder | af-placeholder | string | 'Skriv för att söka...' | Ledtext i sökfältet. |
| size | af-size | 'small' \| 'medium' \| 'large' | 'medium' | Storlek på komponenten. |
| value | (ingen) | string[] | [] | Valda kompetens-id:n. Ska sättas som JavaScript-property, inte som HTML-attribut. |
| popular | af-popular | boolean | true | Visar initiala vanliga eller kontextuella kompetenser när sökningen är tom. |
| afStayOpen | af-stay-open | boolean | false | Håller förslagslistan (dropdown) öppen direkt utan att komponenten behöver fokus (t.ex. i modal eller popup). |
| dataUrl | af-data-url | string | '' | Avancerat läge. URL till kv-base.v1.json. |
Metoder
setOccupationSelection(selection) tar Yrkesväljarens afJobSelected.detail direkt och returnerar de normaliserade occupation-name-ID:n som används som KV-kontext.
Sökbeteende
Tom sökning visar mostCommonSkills, eller en kontextlista om ett giltigt
yrkes-id har angetts. Vid textsökning är hela kompetenskatalogen sökbar.
Direkta textträffar rangordnas före fuzzy-träffar. Fuzzy-sökning kan lägga
till kandidater som inte direktmatchar. Yrkeskontext och förberäknade vikter
påverkar ordningen inom sökmodellens rangordning. Svenska tecken viks inte
ihop, så exempelvis vard och vård behandlas olika.
Programmatisk värdesättning (value)
Förval eller dynamisk värdesättning av valda kompetenser görs via JavaScript-propertyn value som tar en array av Concept ID:n.
const skillSelector = document.querySelector('digi-service-skill-selector');
// Vänta tills komponenten är redo om den nyss lagts till i DOM:en
skillSelector.componentOnReady().then(() => {
// Sätt valda kompetenser
skillSelector.value = ['dmF5_zy7_Utt', 'abc1_def_ghi'];
// Rensa valda kompetenser
// skillSelector.value = [];
});Ogiltiga värden, exempelvis okända id:n, dubletter, tomma strängar eller annat
än en strängarray, avvisas genom att det senaste giltiga värdet återställs och
ett fel skrivs med console.error. Programmatisk ändring av value skickar
inte afKompetensSelection.
Event-output
afKompetensSelection skickas efter användarval, borttagning och rensning.
Eventet returnerar en array med platta kompetensobjekt, eller null när
urvalet har blivit tomt:
[
{
"taxonomy/type": "skill",
"taxonomy/id": "dmF5_zy7_Utt",
"taxonomy/preferred-label": "Berglastning"
}
]Headless-sökning
Samma kompetenssökning utan webbkomponent, för klienter med eget gränssnitt:
import { searchCompetences } from '@arbetsformedlingen/kompetensvaljaren';
// GQSf_fnq_kjF = Truckförare
const competences = await searchCompetences({
query: 'truck',
occupationId: 'GQSf_fnq_kjF',
});Skicka Yrkesväljarens event-output utan egen mappning:
import { searchCompetences } from '@arbetsformedlingen/kompetensvaljaren';
const yv = document.querySelector('digi-service-job-selector');
yv.addEventListener('afJobSelected', async (event) => {
const fromYv = await searchCompetences({
query: 'truck',
occupationSelection: event.detail,
});
// Visa fromYv i klientens gränssnitt.
});query, occupationId och occupationSelection är valfria. Använd antingen
occupationId eller occupationSelection i ett anrop. Det senare tar hela
afJobSelected.detail (en array), eller ett enskilt YV-val när klienten söker
kompetenser separat för varje yrke. Samma normalisering och kontextordning
används av headless och setOccupationSelection(...).
Inledande och avslutande blanksteg i query och occupationId tas bort;
en tom sträng behandlas som ett utelämnat värde.
occupationId ska vara ett Concept ID för occupation-name eller ssyk-level-4
som finns i det dataset som används. För YV-val används occupation-name.id
eller job-title.related.id när related.type är occupation-name.
manual-input och jobbtitlar utan ett kopplat occupation-name bidrar inte
med någon yrkeskontext. Om ingen kontext återstår används den globala sökningen.
| Indata | Beteende |
| --- | --- |
| Endast söktext | Global kompetenssökning. |
| Giltigt yrkes-ID och söktext | Kompetenssökning med yrkesprioritering. |
| Endast giltigt yrkes-ID | Prioriterad kompetenslista enligt KV:s sökpolicy. |
| Varken söktext eller yrkes-ID | KV:s fallbacklista med vanliga kompetenser. |
| Okänt, icke-tomt yrkes-ID, med eller utan söktext | Anropets Promise avvisas med ett Error vars meddelande innehåller Okänt occupationId. |
| Okänt kontext-ID i YV-val | Anropets Promise avvisas på samma sätt, även om andra val är giltiga. |
| Både occupationId och occupationSelection | Anropets Promise avvisas eftersom kontexten är tvetydig. |
Ett okänt ID ger ingen resultatlista och utlöser ingen global fallback.
Även fel vid laddning av basdata eller kontextshards avvisar anropets Promise.
Klienten behöver hantera dessa fel med try/catch eller .catch() och kan
då visa att sökningen misslyckades. En lyckad sökning utan träffar ger däremot [].
Registrerad data via registerSkills(...) används i första hand. Annars
laddas samma Senaste-dataset som för webbkomponenten, med kontextshards vid behov.
Ett ID måste alltså finnas i den registrerade versionen eller i Senaste,
beroende på vilket läge klienten använder.
Resultatet är Promise<KvPublicSelection[]>, med samma publika taxonomiform
som eventet afKompetensSelection och samma sökpolicy som webbkomponenten.
Resultaten returneras i sökmotorns ordning, med högst 20 kompetenser. KV äger data,
sökmotor, fuzzy-policy, shards, cache och maxantal. API:t hanterar inte
gränssnitt, användarval, förslag eller analys/spårning.
Ett Angular-exempel med flera yrken och separata kompetensval visar hur klienten kan koppla ett yrkesval per headless-anrop.
