What is ViewModel & bindingcontext in knockout 3?

Knockout.js: Skab dynamiske brugerflader med custom bindings

16/04/2023

Rating: 3.95 (12678 votes)
Indholdsfortegnelse

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.

What if a binding doesn't work in knockout?
Here are some examples: The binding name should generally match a registered binding (either built-in or custom) or be a parameter for another binding. If the name matches neither of those, Knockout will ignore it (without any error or warning). So if a binding doesn’t appear to work, first check that the name is correct.

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. Brug ko.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 med allBindings.has('bindingsNavn').
  • viewModel: (Deprekeret i Knockout 3.x) Bruges til at tilgå den aktuelle ViewModel. I moderne Knockout bør du bruge bindingContext.$data eller bindingContext.$rawData i stedet.
  • bindingContext: Et objekt, der indeholder binding-konteksten for elementets bindings. Dette objekt inkluderer specielle egenskaber som $parent, $parents og $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- og blur-event handlers. Når elementet får fokus, sættes den bundne observable til true, og når det mister fokus, sættes den til false.
  • update: Overvåger ændringer i den bundne observable. Hvis værdien bliver true, sættes elementet i fokus; hvis den bliver false, 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 i ko.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 via valueAccessor() i din update-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.

Hvad sker der, hvis mit binding-navn er forkert stavet?

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.

Go up