Synchronizace UI se stavem na serveru - refreshEvent
Refesh eventy představují mechanizmus, který udržuje stav zobrazený v adminstraci konzistetní se stavem na serveru. Např. pokud uživatel vytvoří novinku v seznamu, pomocí událostí dojde k zobrazení přidané novinky i všem právě přihlášeným uživatelům, kteří pracují se stejným seznamem.
Ještě častěji je využito k řešení situace, kde z data gridu otevře uživatel editaci položky (nebo její vytvoření), která se otevírá do nového panelu Edee. Po uložení a uzavření editačního panelu jsou pak provedené změnu viditelné i zde.
Implementace probíhá pomocí polling requestů - periodicky je tedy prováděn dotaz na server, zda neexistují nové události
pro zpracování. Jedná se o požadavky na adresu /adm/*/edeeui/userState/get, která vrací případné události jako JSON objekty.
Příklad události
{"event":{ "2":{ "id":2, "event":"change", "type":"tree", "attr":{ "pageLang":"cs", "pageId":1001, "parentId":1 }, "username":null }},"process":{},"message":{}}Událost má následující atributy:
id- identifikátor události - integer sekvence nulovaná při restartu serveruevent- identifikátor akce, případně operace (např.create,update,move,remove)type- typ entity (např.tag,tree,moduleTree)attr- dodatečné atributy - typicky id entity pro možnost cílení událostiusername- případné jméno, pokud je událost cílena na vybraného uživatele
V Javě je událost reprezentovaná objektem com.fg.edee.integration.cps.user.EdeeUiEvent.
konfigurace refresh eventy
Naslouchání na refresh event se specifikuje v metadatech komponenty, konkrétně v datasetu - metadata.dataset.refreshEvent.
event
Primárně se specifikuje název události - tag refreshEvent/event.
Hodnota se skládá z dvou klíčů události event + type (podle JSONU např. changetree) a nebo lze použít pouze type
(podle JSONU např. tree). Na velikosti písmen nezáleží (POZOR, evidujeme problém kdy někdy se to bere v potaz),
typicky se dodržuje camel case konvence.
Je možné uvést i více událostí oddělených čárkou, případně definovat více refreshEvent bloků
<metadata> <dataset mode="combine"> <refreshEvent> <event>updateContentBlock, createContentBlock</event> <validAttributes> <linkedType>#@inheritedMetadataObjectEval[blockPlacementObjectType]#</linkedType> <linkedId>#@inheritedMetadataObjectEval[blockPlacementObjectId]#</linkedId> </validAttributes> </refreshEvent> <refreshEvent> <event>updateContentBlockRelation</event> <validAttributes> <sourceId>#@inheritedMetadataObjectEval[blockPlacementObjectId]#</sourceId> </validAttributes> </refreshEvent> </dataset></metadata>validAttributes
Událost lze omezit pomocí refreshEvent/validAttributes. Aktualizace komponenty pak proběhne pouze pokud atributy
přijaté události a atributy vnořené v konfiguraci jsou totožné.
Využitelné např. v editoru entity, kde se mohou projevit změny jiného uživatele, ale žádoucí jsou pouze změny konrétního záznamu.
puSettings
Aktualizace komponenty je provedena pomocí partial update, tento požadavek lze pomocí refreshEvent/puSettings
dále parametrizovat.
<metadata> <dataset> <refreshEvent> <event>createLocalization</event> <validAttributes> <sourceId>#widget[record].record.primaryKey!0#</sourceId> </validAttributes> <puSettings> <removeFormId>true</removeFormId> <removeFormWidgetData>true</removeFormWidgetData> <type>GET</type> </puSettings> </refreshEvent> </dataset></metadata>zkrácený zápis
Lze použít i zkrácený zápis pouze se jménem události
Je nutné si uvědomit, že bez specifikace atributů bude událost provádět aktualizaci všem aktuálně přihlášeným uživatelům ve všech místech s danou událostí.
<metadata> <dataset mode="combine"> <refreshEvent>createItem</refreshEvent> <refreshEvent>removeItem</refreshEvent> <refreshEvent>updateItem</refreshEvent> </dataset></metadata>podpora
Na přijaté události, reagují widgety ve stránce. Refresh eventy nepodporují automaticky všechny komponenty. Základem je přiřazený dekorátor, který dodává komponentám dynamické funkce.
Komponenty bez decorátoru lze pro refresh využít definicí základního dekorátoru:
<metadata> <dataset> <decorator>fg.ui.Component</decorator> </dataset></metadata>generování události na straně serveru
Pozor - ui refresh událost je na serveru něco jiného než událost modulu ze Spring framework i když jsou někdy spojené, tj. aplikační Spring událost vyvolá i UI událost.
command
Pro generování ui události z commandu lze použít com.fg.edee.integration.web.ui.UiEventFiringCommand.
Připravený command lze použít jako předka na úrovni java kódu, případně lze spojit pomocí agregačního commandu.
Příklad xml specifikace
<command type="aggregation"> <command class="com.fg...MyCommand"/> <command class="com.fg.edee.integration.web.ui.UiEventFiringCommand" uiEventName="update" uiEventType="myItem" /></command>Do verze 10.13.0 lze specifikovat i atributy události
Příklad s asynchronním zpracováním
<command eventName="execute" type="asyncExecutorCommand"> <command class="com.fg...MyCommand"/> <command type="uiEventFiringCommand" uiEventName="update" uiEventType="test" attributeName="testName" attributeValue="1" /></command>Příklad java extenze
public class MyCommand extends UiEventFiringCommand {
public MyCommand() { super("update", "myItem"); }
@Override public Resolution performCommand(RequestContext context, Map<String, Object> parameters) throws CommandExecutionException { // command logic // publish ui event processUiEvent(context); // or publish ui event with custom attrs //publishUiEvent(createEdeeUiEvent(context,ImmutableMap.<String, Serializable>of("id", itemId))); // or since 9.0 with single method call //publishUiEvent(context,ImmutableMap.of("sourceId", sourceIdValue,"blockId",blockIdValue));
return resolution; }
}userService
Událost lze publikovat i přímo z vlastní servisní vrstvy pomocí com.fg.edee.integration.service.UserService.
public class MyService { @Autowired UserService userService;
protected void publishUiEvent(Tag tag, CrudOperation operation) { Map<String,Serializable> attrs = new HashMap<>(); attrs.put("id",tag.getId()); attrs.put("systemId", tag.getSystemId()); userService.publishUiEvent(new EdeeUiEvent(operation.toString(), Tag.E_NAME, attrs)); }}Případně lze definovat factory pro automatickou konverzi Spring událostí na UI události. Pak je nutné provést implementaci třídy
com.fg.edee.integration.cps.user.EdeeUiEventFactory a zajistit registraci do UserService - v rámci edeeui modulu probíhá automaticky.
Ukázka registrace event factory v modulu
import com.fg.webapp.cps.v1.modules.spring.CpsSpringModuleAbstract;
public class MyModule extends CpsSpringModuleAbstract { @Override public void prepareStartModule() { super.prepareStartModule(); UserService userService = getModuleSupport().getModulePublicBean(UserService.class); getModuleContext().getBeansOfType(EdeeUiEventFactory.class) .values() .forEach(userService::registerUiEventFactory); } }Ukázka reálné implementace factory.
public class ContentBlockActiveVersionChangedUiEventFactory implements EdeeUiEventFactory<ContentBlockActiveVersionChangedEvent> {
@Override public EdeeUiEvent create(ContentBlockActiveVersionChangedEvent source) { // create required attrs for possible `validAttributes` filtration final Map<String, Serializable> attr = ImmutableMap.of("blockId", source.getBlockId()); // instantiate event - operation name, entity type, attributes map return new EdeeUiEvent("activeVersionChange", "block", attr); }
@Override public Class[] getSourceClass() { // specify listening events including possible extended implementations return new Class[]{ ContentBlockActiveVersionChangedEvent.class }; }}