I have an scxml event with an "invoke" element. This invoke element updates the datamodel elements when the event is called. How can I make this happen periodically? Is it possible to periodically call this invoke for example, every second? Or perhaps to transition to the event every second?
The problem with the latter is that if I put a transition in the finalize section of the invoke, calling itself, it does not seem to call the invoke section more than once.
I first wanted to provide some background on <invoke>. <invoke> is placed as a child to <state>. It starts a child state machine session on entering the state, and stops the session on exiting the state. Furthermore, if the child session enters a <final> state, then a done.invoke.invokeid event is dispatched on the parent session. You can use this done.invoke.invokeid event in a transition on the parent state, to force the parent state to exit when the child session terminates. Finally, the parent and child states can communicate with each other through <send>. The parent can communicate with the child using <send> with target attribute set to _invoke_invokeid, and the child can communicate with the parent through <send> with target attribute set to _parent.
Now, onto your questions:
This invoke element updates the datamodel elements when the event is called.
I think there may be a problem with the way you have conceptualized this, because the invoked session has its own datamodel (which is to say, its own memory). You can bind the initial datamodel values of the child session using <param>, but you can't really share memory between the parent and child sessions. This means that you can't update the datamodel in the parent session directly in the child session using e.g. <assign> or <script> tags.
The only way for the child session to update the datamodel in the parent session is to communicate with the parent session through passing events (e.g. <send event="update" target="_parent"><param name="dataToUpdate" expr="dataToUpdate"/></send>). The parent then needs to have a <transition> element so that it can process the event sent from the child session, e.g. <transition event="update"><assign location="dataToUpdate" expr="_event.dataToUpdate"/></transition>.
This leads to the question of whether <invoke> is the best, simplest approach to updating the datamodel periodically. It might be simpler to, for example, just put the datamodel update logic inside of a child of <parallel> state. This way, you could use <assign> to update the datamodel directly.
How can I make this happen periodically? Is it possible to periodically call this invoke for example, every second? Or perhaps to transition to the event every second?
To invoke a session periodically, you would enter and exit the state containing the <invoke> element. The following (untested) code would probably work:
<state id=invokeParent">
<!-- this is some data that you want the child session to update in the parent session -->
<datamodel>
<data id="dataToUpdate" />
</datamodel>
<onentry>
<send event="loop" delay="1s"/> <!-- send the 'loop' event every second to loop in invokeParent -->
</onentry>
<transition event="loop" target="invokeParent" /> <!-- this transition will exit and re-enter the state, restarting the invoked session -->
<transition event="done.invoke.myInvoke" target="invokeParent" /> <!-- also loop if the invoked session terminates -->
<invoke id="myInvoke" type="scxml" src="file:test276sub1.scxml"/> <!-- this is the invoke -->
<!-- this targetless transition handles the update event sent from the child session to the parent to update the parent's datamodel -->
<transition event="update">
<assign location="dataToUpdate" expr="_event.dataToUpdate"/>
</transition>
</state>
The problem with the latter is that if I put a transition in the finalize section of the invoke, calling itself, it does not seem to call the invoke section more than once.
I don't believe transition is a legal child of finalize. finalize is meant to contain executable content (e.g. script, assign) that allow you to manipulate events sent by the child session before they are processed by the parent session.
See https://www.w3.org/TR/scxml/#finalize
Related
I have a vue3 webapp that I use to edit some multi-pages documents.
All the pages are stored in a state with pinia. My pinia state is an object with a pages property that is an array containing all the data of each page.
Each page of each document use a specific template, so I created multiple components to handle each template, and I also have subcomponents for some parts that can be found across multiple templates.
I loop through the pages with a root component, passing a reference to the page object, like it :
<PageWrapper v-for="page in pages" :key="page.id" :page="page" />
Then, inside the PageWrapper component, I use the according page template's component, passing along a reference to the page object (the same with subcomponents if any) :
<PageFirst v-if="props.page.type === 'first'" :page="props.page" />
<PageServices v-if="props.page.type === 'services'" :page="props.page" />
<PageTotal v-if="props.page.type === 'total'" :page="props.page" />
<PageContent v-if="props.page.type === 'content'" :page="props.page" />
I wonder what would be the best way to edit a property of my page object from a subcomponent, as I know that it is a bad practice to mutate the property directly.
Do I have to use events ? Is PageWrapper the good place to catch all the events and the modifications?
Any advice on this matter would be of great help to me.
Thanks a lot for your help!
As the Vue official document point out:
In most cases, the child should emit an event to let the parent perform the mutation.
So the answer is you should let the parent do the job of mutating the props. And it will never be a bad solution.
But, look at your design. Your parent component PageWrapper works just like a wrapper. It does nothing but a container for its child.
The props one-way data flow prevents child components from accidentally mutating the parent's state, which can make your app's data flow harder to understand. But if the parent does not actually handle state and your child component's data does not relate with each other, mutating the props inside the child component will be fine.
Note: In order to demonstrate the below issue, I've created an example in Plunker. In there, I'm using a mock server due to the flaw of the writable service from odata.org. Nevertheless, the issue is reproducible with a real server as well.
Currently, I'm binding a child collection to a list relative to an expanded single entity through a navigation property. Something like this:
<Page title="Products"
binding="{
path: 'odataModel>/BusinessPartnerSet(\'0100000000\')',
parameters: {
expand: 'ToProducts'
}
}"
>
<List items="{odataModel>ToProducts}">
<StandardListItem title="{odataModel>ProductName}" />
</List>
</Page>
Now, if I delete an item from the list, the ListBinding sends a batch request containing DELETE and GET. Here is a screenshot of the batchRequestSent event:
The deleted item is gone from the ListBinding and the list is updated as expected. If I have an additional list with the same bindings, then that additional list gets also updated because of the TwoWay data binding (like the Plunker example above). So far so good.
Problem
But the problem is: The parent ContextBinding (in this case, the element binding of the Page) is not updated. This can be seen if you call bindingContext.getProperty("ToProducts") and it will return a list of binding paths in which the ones of the already deleted items are still there. I guess this is because there was no GET request sent from the ContextBinding but from the ListBinding only.
My question
If the change took place in the child ListBinding (either through DELETE or CREATE), how can the parent ContextBinding get notified about the change and update itself without sending an additional request, so that bindingContext.getProperty("ToProducts").length returns always the correct length?
Or differently, when the change occurs, how can I prevent UI5 from sending a GET request from the child ListBinding and let it send the request from the parent ContextBinding instead, so that the change gets propagated to the child ListBinding afterwards automatically?
Is there any standard approach to solve this kind of problem in UI5?
PS: The same problem does not apply to JSON-based bindings. It seems to be OData only.
If someone encounters the same issue: it turned out to be a bug in UI5 which is fixed in 1.46.7+.
[FIX] ODataListBinding: Update expanded list array
If the fix is not available in your current release, one workaround would be to refresh the parent binding by calling parentControl.getElementBinding("modelName").refresh(false, "yourDeferredGroupId") so that the GET request can be sent together with the DELETE request in a single batch request.
Additionally, we can turn off refreshAfterChange (as krisho suggested) so that UI5 doesn't append yet other GET requests for corresponding ListBindings (relatively bound as well as absolutely bound ones). A disadvantage of this approach is that the absolutely bound ListBinding (which has the same content as the relatively bound one) has to be refreshed manually as well - if there is any.
To stop the framework from making the 'GET' call after Delete, you can use the property 'refreshAfterChange' on sap.ui.model.odata.v2.ODataModel, which can be set to 'false'.
After the success of 'Delete', you can 'refresh' the binding on the page, so that all bindings are consistent.
I am wondering if all asynchronous events and callbacks can be traced back to a specific source component by wrapping all component logic (including logic of non-component directives inside component) in a zone, then only that source component needs to be checked for changes along with any child components with changed inputs from the source component, assuming all changes are following the uni-directional flow.
Is this understanding sound?
Is this change detection strategy available in Angular2?
Why is Angular2 detecting changes on all the components after any asynchronous (XHR) event?
One whole Angular application runs in a single zone. Angular uses the zone to patch async APIs and uses notifications from these patched APIs to run change detection every time some async event happened.
The uni-directional flow is for [prop]="value" bindings that works only from parent to child.
Angular runs change detection from root to leafs.
If ChangeDetectionStrategy.OnPush is configured for a component, change detection skips these components (and their descendants) until some binding (inputs) have changed.
There are other strategies to optimize CD.
For example observables and promises, that actively notify about changes and don't need change detection.
Immutable objects which are guaranteed to not change their propery values.
Update
Angular doesn't know what values an event handler has changed. Properties of a component, of a global service, of object references that were passed around, .... It just assumes that when an event handler was called that probably something has changed and then runs a complete change detection cycle to propagate all bindings from parent to child.
Child do parent bindings are events anyway and therefore aren't updated during change detection.
... by wrapping all component logic (including logic of non-component directives inside component) in a zone, then only that source component needs to be checked for changes along with any child components with changed inputs from the source component, assuming all changes are following the uni-directional flow. Is this understanding sound?
When an template-bound event fires – e.g., (click)="doSomething()" – the doSomething() method is free to alter any component or application data. Template statements, such as our doSomething() event handler, are not bound by the unidirectional flow rule, as per the Angular docs:
Responding to events is the other side of Angular's "unidirectional data flow". We're free to change anything, anywhere, during this turn of the event loop.
This is why, by default, Angular's change detection has to check every template binding in every component after an event fires. (Well, after an event within the Angular zone fires.) Angular doesn't know what might have changed... it has to discover what has changed.
The unidirectional flow rule applies to template expressions such as {{some expression}} or [childInputProperty]="parent expression" or if you implement an input property setter method: #Input() set childInputProperty(variableName:type) { ... }.
Is this change detection strategy available in Angular2?
No, because it would severely limit what an event handler could do. Dirty-checking every template binding may not be the most efficient way to detect changes, but it makes it much easier for us to write our event handlers (i.e., to write our applications).
Why is Angular2 detecting changes on all the components after any asynchronous (XHR) event?
Angular doesn't want to limit what we can do in our event handlers. An event handler bound in ComponentA's template can change data that is local to ComponentA, but it can also change data that is in a service (and hence it can change data that is visible to other components), and it can change data in other components, e.g., by calling public APIs/methods on other components.
I'm trying to return to my main flow in spring, from any of my subflows just by clicking a button.
when I use <end-state> it just goes to the previous flow, which is also a sub-flow in the application.
Any ideas?
You just need the appropriate transitions in each subflow-state in the calling flow to do what you want. The end-state id in your subflow is what will be used as the event id you can transition on in your calling flow.
A subflow can be thought of as a branch of execution. So when your subflow is finished, control is returned back to the calling flow. Think of your end-state as a return statement (and the id attribute as the value returned -- you can also set output attributes but that is not important here).
When your subflow terminates, control is returned backed to the calling flow. The calling flow should define a transition that determines how to handle this event. The event id you will see is the id of the end-state in your subflow.
So in your subflow if you have the following end-state:
<end-state id="back"/>
You can then handle this transition in the flow that called the subflow:
<subflow-state id="do-some-sub" flow="some-sub">
< ... input variables, expressions, etc ... />
<transition on="back" to="some-state"/>
</subflow-state>
Note that some-state here can also be an end-state. Your situation sounds like you have a main flow that calls a subflow which in turn calls another subflow. So you would want some-state to be an end-state, which would then be handled by its calling flow (in your case the "main" flow).
You can achieve this by adding the 'parent' attribute when you define your sub flow like this
<flow parent="login" xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<view-state id="forgotPassword">
<transition on="backtoLogin" to="login">
</transition>
here i want my subflow to return to the login page on a back button 'click' .
One thing to notice is that in your parent flow.xml u need to specify the absolute path of the view
my parent ie login-flow.xml is given below
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<view-state id="login" view="../login/defaultLogin.xhtml">
Let's take the next example:
<mx:State name="sayHello">
<mx:SetProperty name="preText" target="{this}" value="Hello"><mx:SetProperty>
</mx:State>
Can I somehow know when preText property has been set to hello?
Already tried with:
state->activate
state->enterState
state->exitState
and
UIComponent->currentStateChange
In all the cases above,pretext property is null, how ever somehow, later on it gets the desired value (I tested with a timer)
Any help would be great!
Thanks!
It is tough to say. Most properties implemented by the Flex Framework dispatch a propertyChangedEvent. So, in this case you could listen to preTextChanged event on the component in question to let you know that the property changed.
If this is a property you implemented yourself, just make the properties set method dispatch the event, like this:
dispatchEvent(new Event('preTextChanged'));
Add the listener like this:
this.addEventListener('preTextChanged',onpreTextChanged);
You won't be able to listen to the property change event in MXML if you don't define event metadata for the event; and most components do not bother to define metadata for the propertyChanged event.
The act of changing a state can take time. Due to the asynchronous nature of Flex/Flash Player something like this:
currentState = newState
trace(preText);
The trace value will most likely not be set yet because the state change processing did not occur yet. You may be able to listen to the currentStateChange event, thoug. When that dispatches your properties should all be modified.