16/04/2023
- Introduktion til Custom Bindings i Knockout.js
- Hvad er ViewModel og BindingContext?
- Registrering af dine Custom Bindings
- Parametre i Callbacks
- init Callback: Opsætning og Initialisering
- update Callback: DOM-manipulation og Reaktion på Ændringer
- Avancerede Teknikker og Overvejelser
- Fordele ved Custom Bindings
- Konklusion
- Ofte Stillede Spørgsmål (FAQ)
Introduktion til Custom Bindings i Knockout.js
Knockout.js er et populært JavaScript-bibliotek, der gør det nemt at skabe dynamiske og responsive brugerflader baseret på data. Mens Knockout leveres med en række indbyggede bindings som click, value og visible, tilbyder det også en utrolig fleksibilitet gennem muligheden for at skabe dine egne custom bindings. Dette giver udviklere magten til at definere præcist, hvordan observables interagerer med DOM-elementer, hvilket muliggør indkapsling af kompleks adfærd i let genanvendelige komponenter. Forestil dig at bygge interaktive elementer som avancerede datatabeller, fanebladsnavigationer eller specialiserede inputfelter – alt sammen ved at definere dine egne bindings.

Hvad er ViewModel og BindingContext?
Før vi dykker ned i custom bindings, er det vigtigt at forstå de centrale begreber i Knockout: ViewModel og BindingContext.
ViewModel
ViewModel'en er hjertet i din Knockout-applikation. Det er et JavaScript-objekt, der indeholder dine data (typisk som observables og computed observables) og den logik, der styrer din brugerflade. Knockout bruger ViewModel'en til at forbinde dine data til HTML-elementerne via data-bind-attributten.
BindingContext
BindingContext er et objekt, der giver adgang til ViewModel'en og dens hierarkiske struktur. Hver gang du bruger en binding, er der en tilknyttet BindingContext. Den vigtigste egenskab ved BindingContext er $data, som refererer til den aktuelle ViewModel, der er bundet til elementet. Derudover tilbyder BindingContext særlige egenskaber som $parent, $parents (et array af alle parent-kontekster) og $root (refererer til den øverste rod-ViewModel), hvilket giver dig mulighed for at navigere i hierarkiet og tilgå data på forskellige niveauer i din applikation.
Registrering af dine Custom Bindings
At skabe et custom binding i Knockout er en ligetil proces. Du registrerer dit nye binding ved at tilføje det som en subegenskab til det globale ko.bindingHandlers-objekt. Dette objekt fungerer som et bibliotek, hvor du kan definere alle dine brugerdefinerede bindings.
Strukturen for et custom binding involverer typisk mindst én af to callbacks: init og update.
ko.bindingHandlers.ditEgetNavn = { init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { // Denne funktion kaldes én gang, når bindingen først anvendes på et element. // Bruges til at initialisere tilstand, opsætte event handlers osv. }, update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { // Denne funktion kaldes første gang, når bindingen anvendes, // og derefter hver gang nogen observables eller computed observables, der tilgås heri, ændrer sig. // Bruges til at opdatere DOM-elementet baseret på de leverede værdier. } }; Når du har defineret dit binding, kan du bruge det på ethvert DOM-element ved hjælp af data-bind-attributten:
<div data-bind="ditEgetNavn: dinObservableVærdi"> </div>Det er vigtigt at bemærke, at du ikke behøver at implementere både init og update. Du kan vælge kun at inkludere den callback, der er nødvendig for din specifikke funktionalitet.
Parametre i Callbacks
Både init og update callbacks modtager et ensartet sæt parametre, der giver adgang til den nødvendige information:
- element: DOM-elementet, som bindingen er knyttet til.
- valueAccessor: En JavaScript-funktion. Ved at kalde denne funktion (
valueAccessor()) får du den aktuelle ViewModel-egenskab, der er involveret i bindingen. Brugko.unwrap(valueAccessor())for nemt at håndtere både observables og almindelige værdier. - allBindings: Et objekt, der giver adgang til alle de ViewModel-værdier, der er bundet til det pågældende DOM-element. Du kan hente værdien af en specifik binding med
allBindings.get('bindingsNavn')eller tjekke, om en binding eksisterer medallBindings.has('bindingsNavn'). - viewModel: (Deprekeret i Knockout 3.x) Bruges til at tilgå den aktuelle ViewModel. I moderne Knockout bør du bruge
bindingContext.$dataellerbindingContext.$rawDatai stedet. - bindingContext: Et objekt, der indeholder binding-konteksten for elementets bindings. Dette objekt inkluderer specielle egenskaber som
$parent,$parentsog$root, der muliggør navigation i ViewModel-hierarkiet.
init Callback: Opsætning og Initialisering
init-callbacken kaldes kun én gang for hvert DOM-element, hvor bindingen anvendes. Dens primære formål er:
- Initialisering af DOM-elementets tilstand: Her kan du sætte den indledende visning eller konfiguration af elementet, før dataene er fuldt ud interaktive.
- Registrering af Event Handlers: Du kan opsætte event listeners (f.eks.
click,mouseover,input) på elementet. Når en begivenhed udløses, kan du bruge disse handlers til at opdatere den tilknyttede observable i din ViewModel. Dette muliggør tovejskommunikation, hvor brugerinteraktioner på DOM'en afspejles i dine data.
Eksempel: Tovejs-binding med hasFocus
Lad os se på et eksempel, hvor vi skaber et hasFocus-binding, der binder et elements fokus-tilstand til en observable:
ko.bindingHandlers.hasFocus = { init: function(element, valueAccessor) { var value = valueAccessor(); $(element).focus(function() { value(true); }).blur(function() { value(false); }); }, update: function(element, valueAccessor) { var value = valueAccessor(); if (ko.unwrap(value)) { element.focus(); } else { element.blur(); } } }; I dette eksempel:
- init: Registrerer
focus- ogblur-event handlers. Når elementet får fokus, sættes den bundne observable tiltrue, og når det mister fokus, sættes den tilfalse. - update: Overvåger ændringer i den bundne observable. Hvis værdien bliver
true, sættes elementet i fokus; hvis den bliverfalse, fjernes fokus.
Dette gør det muligt at bruge bindingen på følgende måde:
<p>Navn: <input data-bind="hasFocus: redigererNavn" /></p><div data-bind="visible: redigererNavn">Du redigerer navnet.</div><button data-bind="enable: !redigererNavn(), click: function() { redigererNavn(true) }">Rediger navn</button><script type="text/javascript"> var viewModel = { redigererNavn: ko.observable() }; ko.applyBindings(viewModel); </script>Her kan vi både læse og skrive fokus-tilstanden for inputfeltet. Når inputfeltet er i fokus, bliver redigererNavn sat til true, hvilket viser en besked. Knappen er aktiveret, når redigererNavn er false, og giver mulighed for at sætte fokus manuelt.
update Callback: DOM-manipulation og Reaktion på Ændringer
update-callbacken er kernen i dynamiske ændringer i din brugerflade. Den kaldes første gang bindingen anvendes, og derefter hver gang en af de observables, der er tilgået inde i callbacken, ændres. Dette gør det muligt at opdatere DOM-elementet for at afspejle den aktuelle tilstand af dine data.
Eksempel: Animeret synlighed med slideVisible
Lad os udvide det velkendte visible-binding ved at skabe et slideVisible-binding, der bruger jQuerys slideDown og slideUp til at animere elementets synlighed:
ko.bindingHandlers.slideVisible = { update: function(element, valueAccessor, allBindings) { var isVisible = ko.unwrap(valueAccessor()); var duration = allBindings.get('slideDuration') || 400; // Standard varighed på 400ms if (isVisible) { $(element).slideDown(duration); } else { $(element).slideUp(duration); } } }; For at bruge dette binding:
<div data-bind="slideVisible: gaveindpakning, slideDuration: 600">Du har valgt gaveindpakning.</div><label> <input type="checkbox" data-bind="checked: gaveindpakning" /> Gaveindpakning </label><script type="text/javascript"> var viewModel = { gaveindpakning: ko.observable(true) }; ko.applyBindings(viewModel); </script>Her vil div-elementet glide ind og ud af syne, når status for gaveindpakning-observablen ændres via afkrydsningsfeltet. Vi bruger allBindings.get('slideDuration') til også at kunne styre animationsvarigheden via en separat binding.
Vigtigt: For at animationerne skal fungere, skal du sikre dig, at jQuery er inkluderet i dit projekt.
Avancerede Teknikker og Overvejelser
Håndtering af Flere Bindings
Når du opretter et custom binding, der har brug for information fra andre bindings på samme element, er allBindings-parameteren din bedste ven. Den giver adgang til alle andre bindings, hvilket muliggør komplekse interaktioner mellem forskellige UI-kontroller.
Binding Syntax og Værdier
Knockouts deklarative bindingssystem tillader en fleksibel brug af data-bind-attributten. En binding består af et navn og en værdi, adskilt af et kolon (f.eks. text: minTekst). Flere bindings på samme element adskilles af kommaer (f.eks. text: minTekst, visible: erSynlig).
Binding-værdien kan være alt fra en simpel variabel til komplekse JavaScript-udtryk, funktionskald eller objektliteraler:
- Variabel:
data-bind="text: brugernavn" - JavaScript-udtryk:
data-bind="text: pris() > 100 ? 'Dyr': 'Billig'" - Funktionskald:
data-bind="click: function(data) { minFunktion(data.id) }" - Objektliteral:
data-bind="with: { navn: 'Test', alder: 30 }"
Knockout er ret tolerant over for whitespace og JavaScript-kommentarer i data-bind-attributten, hvilket giver dig frihed til at strukturere dine bindings læsbart.
Håndtering af Fejl i Bindings
Hvis en binding ikke ser ud til at virke, er her nogle punkter, du kan tjekke:
- Korrekt Binding Navn: Sørg for, at navnet på din binding (f.eks.
ditEgetNavn) er stavet korrekt og matcher det navn, du har registreret iko.bindingHandlers. Hvis navnet ikke genkendes, ignorerer Knockout bindingen uden fejlmeddelelse. - Gyldige Binding Værdier: Tjek, at den værdi, du giver til bindingen, er et gyldigt JavaScript-udtryk. Hvis udtrykket er ugyldigt eller refererer til en ukendt variabel, vil Knockout generere en fejl og stoppe behandlingen af bindings.
- Observable eller Værdi?: Husk at bruge
ko.unwrap(), når du tilgår værdier viavalueAccessor()i dinupdate-callback, for at sikre, at du altid får den faktiske værdi, uanset om den er en observable eller en almindelig værdi.
Virtuelle Elementer
Knockout understøtter også en syntaks kaldet virtuelle elementer, som kan være nyttig for bindings, der opererer på grupper af elementer eller for at undgå unødvendige div-tags. Hvis dit custom binding skal fungere med virtuel-element-syntaksen (f.eks. ... ), henvises til Knockouts officielle dokumentation om virtuelle elementer for specifikke implementeringsdetaljer.
Fordele ved Custom Bindings
- Genanvendelighed: Når du har skabt et custom binding, kan det nemt genbruges på tværs af din applikation eller i andre projekter.
- Abstraktion: Kompleks DOM-manipulation eller logik kan indkapsles i et simpelt binding, hvilket holder din HTML ren og din JavaScript-kode organiseret.
- Udvidelse af Knockout: Du kan tilpasse Knockout til at håndtere specifikke UI-mønstre eller integrationer med tredjepartsbiblioteker (som jQuery i vores
slideVisible-eksempel). - Forbedret Læsbarhed: Ved at navngive dine bindings meningsfuldt, kan din HTML blive mere selvforklarende.
Konklusion
Custom bindings er en af de mest kraftfulde funktioner i Knockout.js. De giver dig friheden til at udvide bibliotekets standardfunktionalitet og skabe skræddersyede, genanvendelige UI-komponenter. Ved at mestre init og update callbacks, samt forstå valueAccessor og allBindings, kan du bygge sofistikerede og dynamiske webapplikationer, der reagerer elegant på brugerinteraktioner og dataændringer.
Ofte Stillede Spørgsmål (FAQ)
Hvad er forskellen på init og update?
init kaldes kun én gang, når bindingen først anvendes, og bruges til initial opsætning og event handling. update kaldes ved initial anvendelse og hver gang en afhængig observable ændrer sig, og bruges til at opdatere DOM'en baseret på dataændringer.
Hvornår skal jeg bruge ko.unwrap()?
Brug ko.unwrap() på værdien returneret af valueAccessor() for at få den aktuelle værdi, uanset om den er en observable eller en almindelig JavaScript-værdi. Dette forenkler din kode og sikrer, at den fungerer korrekt i begge tilfælde.
Kan jeg bruge custom bindings med andre biblioteker som jQuery?
Ja, absolut! init-callbacken er det ideelle sted at integrere med JavaScript-biblioteker som jQuery for at udføre DOM-manipulationer eller opsætte avancerede interaktioner.
Hvis navnet på din binding ikke matcher nogen registreret binding i ko.bindingHandlers, vil Knockout simpelthen ignorere bindingen uden at give en fejlmeddelelse. Det er derfor vigtigt at dobbelttjekke stavemåden.
Hvordan får jeg adgang til en ViewModel fra en parent-binding?
Brug bindingContext.$parent, bindingContext.$parents eller bindingContext.$root inde i din binding-callback for at navigere op i ViewModel-hierarkiet og få adgang til data fra parent-kontekster.
Hvis du vil læse andre artikler, der ligner Knockout.js: Skab dynamiske brugerflader med custom bindings, kan du besøge kategorien Teknologi.
