Elementtiyhdistelmä
ElementComposite-luokka käärii mukautetun HTML-elementin tai web-komponentin. Se sitoo Java-luokkasi taustalla olevaan Element-elementtiin ja antaa sinun työskennellä kyseisen elementin ominaisuuksien, attribuuttien ja tapahtumien kanssa Javassa. Käytä sitä, kun integroi web-komponentteja webforJ-sovellukseen.
ElementComposite-luokkaaValitse ElementComposite, kun käärit kolmannen osapuolen web-komponentin, jota webforJ ei jo tarjoa. Jos sisäänrakennettu webforJ-komponentti kattaa käytön (kuten TextField, ColorField, Button jne.), käytä sitä sen sijaan. Yksittäisille DOM-toimille, joita ei tarvitse käyttää uudelleen, Element-luokkaa voidaan käyttää suoraan ilman käärettä.
Tässä oppaassa esitellään, kuinka toteutetaan Web Awesome suhteellisen ajan web-komponentti käyttämällä ElementComposite-luokkaa.
Näytä koodi
- RelativeTimeView.java
Luokan anotoinnit
Kolme annotaatiota esiintyy yleisesti ElementComposite-aliluokan yläosassa: @NodeName määrittelee HTML-tagin, jota komponentti käärii, ja @JavaScript sekä @StyleSheet lataavat kaikki asiakaspuolen resurssit, joita taustalla oleva web-komponentti tarvitsee. @NodeName on pakollinen ja spesifinen ElementComposite:lle. @JavaScript ja @StyleSheet ovat yleisiä webforJ-resurssianotointeja ja toimivat kaikissa luokissa, mukaan lukien näkymät, komponentit tai App-luokka.
@NodeName
@NodeName-annotaatio määrittelee HTML-tagin, jota komponentti käärii. WebforJ käyttää tätä nimeä luodessaan taustalla olevaa elementtiä DOM:ssä.
@NodeName("wa-relative-time")
public class RelativeTime extends ElementComposite {
// ...
}
Tagin nimen on vastattava mukautettua elementtiä, joka on rekisteröity asiakaspuolella. Ilman tätä annotaatiota kehys ei voi määrittää, mikä elementti luodaan.
Aliluokassa getNodeName() palauttaa ilmoitetun tagin ja getElement() palauttaa taustalla olevan Element:in, jotta voit kutsua suoraan DOM-tason metodeja.
@JavaScript
@JavaScript-annotaatio lataa skriptin, joka määrittelee tai rekisteröi taustalla olevan web-komponentin. Aseta se luokkaan, jotta skripti ladataan vain, kun komponenttia käytetään.
@NodeName("wa-relative-time")
@JavaScript("https://ka-f.webawesome.com/webawesome@3.12.0/webawesome.loader.js")
public class RelativeTime extends ElementComposite {
// ...
}
Useita @JavaScript-annotaatioita on sallittuja, ja webforJ poistaa duplikaatit automaattisesti. Sama skripti ei lataudu kahdesti, jos useat komponentit riippuvat siitä.
Katso JavaScript-tiedostojen tuonti täydellisestä vaihtoehtojen joukosta, mukaan lukien top, attributes ja latausaika.
@StyleSheet
@StyleSheet-annotaatio lataa CSS-tiedoston, jolle komponentti riippuu. Se on hyödyllinen kolmansien osapuolten komponenteille, jotka toimittavat erillisen tyylitiedoston tai komponenttispesifisten tyylien pakettien kokoamiseen kääreen mukana.
@StyleSheet("https://ka-f.webawesome.com/webawesome@3.12.0/styles/themes/default.css")
Paikallisesti pakattuja resursseja varten käytä ws://-etuliitettä viitataksesi tiedostoihin resources/static-kansiossa:
@StyleSheet("ws://components/relative-time.css")
Katso CSS-tiedostojen tuonti täydelliselle vaihtoehtojen joukolle.
Ominaisuudet ja attribuuttideskryptorit
Ominaisuudet ja attribuutit edustavat web-komponentin tilaa, yleensä pitäen tietoja tai konfiguraatiota. ElementComposite altistaa molemmat PropertyDescriptorin kautta.
Kaksi tehdasmetodia PropertyDescriptorissa tuottaa itse deskriptorin, yksi per sitomiskohde:
PropertyDescriptor<T> property = PropertyDescriptor.property(String name, T defaultValue);
PropertyDescriptor<T> attribute = PropertyDescriptor.attribute(String name, T defaultValue);
PropertyDescriptor.property() sitoo JavaScript-ominaisuuteen DOM-solmussa. PropertyDescriptor.attribute() sitoo HTML-attribuuttiin. Ensimmäinen argumentti on nimi, jota web-komponentti odottaa. Toinen on oletusarvo, joka myös määrittää deskriptorin Java-tyypin.
Määritä deskriptor komponentin yksityiseksi kentäksi ja lue ja kirjoita sen kautta set(PropertyDescriptor<V> property, V value) ja get(PropertyDescriptor<V> property) -metodeilla.
Ominaisuudet ovat sisäistä tilaa DOM-solmussa eivätkä heijastu merkkaustekstiin. Attribuutit ovat HTML-merkkauksia, jotka ovat näkyviä ulkoisille skripteille ja CSS:lle.
// Esimerkki ominaisuudesta nimeltä "title" ElementComposite-luokassa
private final PropertyDescriptor<String> title = PropertyDescriptor.property("title", "");
// Esimerkki attribuutista nimeltä "value" ElementComposite-luokassa
private final PropertyDescriptor<String> value = PropertyDescriptor.attribute("value", "");
//...
set(title, "My Title");
set(value, "My Value");
Yllä olevat kutsut käyttävät set()-metodia suoraan esittelemään primitiivistä muotoa. Käytännössä set() ja get() ovat protected-menetelmiä ElementComposite:ssa. Ne ovat primitiivinen kerros, joka synkronoi Java-arvoja taustalla olevan elementin kanssa, ei julkinen API, jota kuluttajat kutsuvat. Tarkoitettu malli on pitää PropertyDescriptor yksityisenä ja kirjoittaa julkiset setX() ja getX() -metodit, jotka delegoivat primitiivisiin kutsuihin.
@NodeName("my-card")
public class Card extends ElementComposite {
private final PropertyDescriptor<String> heading =
PropertyDescriptor.property("heading", "");
public Card setHeading(String value) {
set(heading, value); // suojattu primitiivi
return this;
}
public String getHeading() {
return get(heading); // suojattu primitiivi
}
}
Yksi kutsu set(descriptor, value) tekee kolme asiaa kerralla. Se työntää arvon asiakkaalle setProperty()-menetelmän kautta ominaisuuksille tai setAttribute()-menetelmän kautta attribuuteille. Se tallentaa arvon paikalliseen palvelinpuolen välimuistiin, yksi kartta per komponentti-instanssi. Ja se tallentaa aikarajan tyypin arvon rinnalle, jotta myöhemmät get()-kutsut tietävät, kuinka deserialisoida.
Tuo paikallinen välimuisti on syy siihen, että get() voi olla edullinen oletusarvoisesti. get(descriptor) palauttaa välimuistissa olevan arvon palvelinpuolen tallennuksesta ilman verkko-kutsua, koska jokainen set() pitää välimuistin synkronoituna asiakkaan kanssa. Valinnainen boolean toinen argumentti hallitsee, ohitetaanko välimuisti ja luetaanko se selaimesta sen sijaan.
String cached = get(heading); // lukee palvelinpuolen välimuistista
String live = get(heading, true); // pakottaa lukemaan selaimesta
Aseta fromClient todeksi, kun arvo voi muuttua asiakkaalla ilman palvelimen tietoa, kuten kirjoitetun <input>-arvon yhteydessä. Palvelinohjatuille ominaisuuksille oletus väistää täyttä kierrosta.
Valinnainen kolmas argumentti on java.lang.reflect.Type ja hallitsee, miten tulos deserialisoidaan. webforJ ratkaisee tyypin tässä järjestyksessä: eksplisiittinen Type-argumentti, jos se on annettu, sitten aikarajan tyyppi, joka tallennettiin aiemmalla set()-kutsulla samalla deskriptorilla, sitten Object.class. Käytännössä aiemmin tallennettu tyyppi set()-kutsusta riittää, joten kolmas argumentti voidaan yleensä jättää pois. Se on tarpeen, kun tallennettu luokka menettää tietoa, jota deserialisoija tarvitsee, kuten parametrisoitu tyyppi kuten List<String>, jonka aikaraja on vain ArrayList.
Alla oleva demo lisää suhteellisia-aika-ominaisuuksia web-komponentin asiakirjojen mukaan ja altistaa ne gettereiden ja setterien läpi. Jokainen aktiviteettisyötteen rivi käyttää erilaisia format ja numeric arvoja näyttääkseen, kuinka sama komponentti renderöidään eri konfiguraatioissa.
Näytä koodi
- RelativeTimePropertiesView.java
- activityfeed.css
Ominaisuudet vs attribuutit
Vaikka PropertyDescriptor.property() ja PropertyDescriptor.attribute() vaikuttavat olevan vaihdettavissa, ne kohdistavat eri osiin taustalla olevaa elementtiä. Väärän valinnan tekeminen johtaa arvoihin, jotka hiljaa epäonnistuvat sovelluksessa.
Ominaisuudet ovat JavaScript-objektin ominaisuuksia DOM-solmussa. Ne voivat pitää minkä tyyppisiä tahansa, mukaan lukien merkkijonot, booleanit, numerot, objektit ja taulukot, ja ne edustavat elementin nykyistä aikarajan tilaa. Ominaisuuden asettaminen on suora JavaScript-määritys.
Attribuutit ovat HTML-merkkauksia. Ne sijaitsevat elementin avautuvassa tagissa, ovat aina merkkijonoja ja edustavat elementin alkukonfiguratia. Attribuutin asettaminen laukaisee DOM-muutoksen ja merkkijonon muunnoksen.
Joissakin tapauksissa kaksi pysyy synkronoituna. Toisissa ne eroavat. <input>-elementin value on klassinen esimerkki: value-attribuutti on alkuperäinen arvo, kun taas value-ominaisuus on nykyinen arvo, jonka käyttäjä on kirjoittanut. Attribuutin lukeminen sen jälkeen, kun käyttäjä on kirjoittanut, palauttaa alkuperäisen merkkaustekstin, mutta ominaisuuden lukeminen palauttaa kentän nykyisen sisällön.
Käytä ominaisuuksia:
- Usein vaihtuva aikarajan tila: laskurit, nykyiset valinnat, kirjoitetut arvot
- Ei-merkkijonotyypit: booleanit, numerot, objektit, taulukot
- Suorituskykyherkät päivitykset: ominaisuudet ohittavat attribuuttien vaatimien merkkijonomuunnosten
Käytä attribuutteja:
- Alkukonfiguraatio: asetukset, jotka komponentti lukee kerran, kun se yhdistetään
- CSS-valitsimet: arvot, joita haluat kohdistaa valitsimilla kuten
[disabled]tai[variant="danger"] - Esteettömyyshookit:
aria-label,roleja muut ARIA-attribuutit - Harvoin muuttuvat merkkijonon kaltaiset asetukset
Kun käännät kolmannen osapuolen web-komponenttia, tarkista komponentin dokumentaatio varmistaaksesi, mikä nimi liittyy ominaisuuteen ja mikä attribuuttiin. PropertyDescriptor.attribute()-kutsun käyttäminen jollekin, jota komponentti altistaa vain ominaisuutena, ei toimi, ja sama pätee käänteisesti. Komponentti hiljaa jättää arvon huomiotta.
Ominaisuuksien tyypitys
Deskriptorin parametrina on arvon Java-tyyppi. Täysi ilmoitussynntaksi on:
private final PropertyDescriptor<T> name =
PropertyDescriptor.property(String name, T defaultValue);
<T>-geneerinen parametri määrittelee arvon tyypin. Oletusarvon aikarajan tyyppi määrittää myös T:n, joten geneeristä argumenttia ei yleensä tarvitse määrittää eksplisiittisesti. webforJ käyttää T:tä arvojen serialisoimiseen ja deserialisoimiseen asiakkaan kanssa kommunikoidessa.
private final PropertyDescriptor<String> label =
PropertyDescriptor.property("label", "");
private final PropertyDescriptor<Boolean> disabled =
PropertyDescriptor.property("disabled", false);
private final PropertyDescriptor<Integer> max =
PropertyDescriptor.property("max", 100);
private final PropertyDescriptor<Double> step =
PropertyDescriptor.property("step", 1.0);
Serialisointi on automaattista primitiiveille, niiden pakatuille vastineille ja String:lle. Monimutkaisille tyypeille arvo serialisoidaan JSON-muodossa ennen kuin se asetetaan asiakkaan puolella olevalle omaisuudelle.
Arvojen validoiminen
Vahvista arvot setterissä ennen set()-kutsun tekemistä. Setter on luonnollinen valvontakohta, koska jokainen muutos kulkee sen läpi.
private final PropertyDescriptor<Integer> max =
PropertyDescriptor.property("max", 100);
public Slider setMax(int value) {
if (value < 0) {
throw new IllegalArgumentException("max must be non-negative");
}
set(max, value);
return this;
}
Nulloitettavien viitteiden yhteydessä käytä Objects.requireNonNull()-metodia, jotta epäonnistuminen tulee näkyväksi rajapinnassa sen sijaan, että se tapahtuisi myöhemmin renderöintiputkessa.
public Card setHeading(String value) {
Objects.requireNonNull(value, "heading cannot be null");
set(heading, value);
return this;
}
Vältä vahvistamista get()-kutsussa. Lukujen tulisi pysyä edullisina ja johdonmukaisina.
Enum-tyyliset ominaisuudet
Suurin osa web-komponenteista odottaa pienistä tai kebab-käsen merkkijonoarvoista enum-tyylisille ominaisuuksille (theme="primary", expanse="xs"). webforJ käyttää Gsonia enumien serialisoimiseen, mutta Gsonin oletusesitys on vakion nimi isoilla kirjaimilla. Merkitse jokainen vakio @SerializedName-annotaatiolla, jotta serialisoitu arvo vastaa sitä, mitä web-komponentti odottaa.
import com.google.gson.annotations.SerializedName;
public enum Variant {
@SerializedName("primary")
PRIMARY,
@SerializedName("secondary")
SECONDARY,
@SerializedName("danger")
DANGER
}
Määritä deskriptorin enum-tyyppi ja käytä enumia suoraan setterissä ja getterissä.
private final PropertyDescriptor<Variant> variant =
PropertyDescriptor.property("variant", Variant.PRIMARY);
public MyButton setVariant(Variant value) {
set(variant, value);
return this;
}
public Variant getVariant() {
return get(variant);
}
Tämä on sama malli, jota webforJ:n sisäänrakennetut komponentit käyttävät Theme, Expanse ja vastaavien enum-tyyppien osalta. Julkinen Java-API pysyy tyyppiturvallisena, ja arvo, jonka web-komponentti vastaanottaa, on merkkijono @SerializedName-annotaatiosta.
Ominaisuuksien testaaminen
PropertyDescriptorTester validoi, että jokainen PropertyDescriptor komponentissa on kytketty oikein. Se skannaa luokan deskriptorikentät, kutsuu kutakin setteria oletusarvolla ja vertaa tulosta siihen, mitä getter palauttaa. Testeri löytää yhdistämisvirheitä ennen niiden saavuttamista toimivassa sovelluksessa: setter, joka kirjoittaa väärään deskriptorin, getter, joka lukee eri ominaisuutta, oletusarvo, joka ei palauta takaisin, tai puuttuva pääsy ilmoitetulle deskriptorille.
Perustason testi komponentille näyttää tältä:
import com.webforj.component.element.PropertyDescriptorTester;
import org.junit.jupiter.api.Test;
class CardTest {
@Test
void validateProperties() {
Card component = new Card();
PropertyDescriptorTester.run(Card.class, component);
}
}
Ominaisuuksien jättämisen
Jotkut deskriptorit eivät noudata standardi getter- ja setter-käytäntöjä, tai ne riippuvat ulkoisesta tilasta, jota testi ei voi tyydyttää. Merkitse ne @PropertyExclude-annotaatiolla ohittaaksesi ne.
@PropertyExclude
private final PropertyDescriptor<String> internal =
PropertyDescriptor.property("internal", "");
Mukautetut getter- ja setter-nimet
Jos deskriptorissa käytetään ei-standardin pääsy nimiä, julista ne @PropertyMethods-annotaatiolla.
@PropertyMethods(getter = "retrieveValue", setter = "updateValue")
private final PropertyDescriptor<String> custom =
PropertyDescriptor.property("custom", "default");
target-parametri hyväksyy luokan, kun pääsyt sijaitsevat muualla kuin itse komponentissa.
Lisätietoa testauspinnasta, katso PropertyDescriptorTester.
Huolenaiheiden rajapinnat
Huolenaiheiden rajapinnat antavat ElementComposite-aliluokalle ominaisuuksia ilman, että sinun tarvitsee kirjoittaa toteutusta itse. Rajapinnat välittävät kutsuja taustalla olevalle elementille. Toteuta ne, joita komponentin tulisi tukea, parametrisoituna aliluokan tyyppillä, jotta ketjukutsu palauttaa komponentin:
@NodeName("my-badge")
public class MyBadge extends ElementComposite
implements HasText<MyBadge>, HasClassName<MyBadge>, HasStyle<MyBadge> {
// Ei toteutusta tarvita.
}
MyBadge badge = new MyBadge()
.setText("New")
.addClassName("highlight")
.setStyle("color", "var(--dwc-color-primary)");
Yllä olevat kolme rajapintaa kattavat kaiken, mitä MyBadge tarvitsee ilman luokan sisällä mitään menetelmäkehoituksia. HasText altistaa setText()-kutsun ja kirjoittaa elementin tekstisisältöön. HasClassName altistaa addClassName(), joka mahdollistaa tarran kohdentamisen CSS:st