Showing global spinner during data fetching - react-query

I have components tree like:
<App>
<Loader show={...}/>
<Form>
<input name="val" />
<input type="submit" />
</Form>
<Data input={...}>
Data is {here is data fetched for props.input}
</Data>
</App>
I'm using hook useQuery from react-query in my Data component to fetch data for input and it works, but i have access to isLoading property within Data component and i want to have one global spinner which would be showed during any data request. Is it possible to do it using react-query ?
I thought about wrap App in Suspense and set Loader as fallback and enable suspense in useQuery call, but maybe there is any other way to do it ?

next to isLoading, useQuery also returns an isFetching Boolean, which will be true whenever a request is „in flight“ for the query, including background updates (isLoading will only be true for the „initial“ loading when you have no data yet).
If you want a global loading spinner, there is also the useIsFetching hook, which will return the amount of queries that are currently fetching. See: https://react-query.tanstack.com/reference/useIsFetching

Related

Can I pass parameters to a content slot from the calling ISML?

I'd like to be able to pass some supplementary information to a Content Slot, either through a request scope variable or some other means.
I've tried this in my calling ISML:
<isset name="message" scope="request" value="I want to be an Air Force Ranger" />
<isslot id="slot-message" context="global" description="banner"/>
And in the rendering template for the slot I have:
<iscontent type="text/html" charset="UTF-8" compact="true"/>
<iscache type="relative" hour="24"/>
<h3>${request.custom.message}</h3>
However, in the output HTML, I just get:
<h3>null</h3>
Is there some way I can pass an Object or String to a Content Slot?
Content asset don't have access to the data created or passed to ISML. However, a workaround can be done by adding the data to the DOM and then reading it inside the content asset:
<div class="banner-data" data-message="${message}">
<isslot id="slot-message" context="global" description="banner"/>
</div>
Then, in your content asset, you can read the message and use it:
<script>
var bannerData = $('.banner-data').data();
var message = bannerData["message"];
</script>
Content Assets is not aware of the page they are embedded in. This is by design. You can however embed HTML inside your content by using $include()$ which fetches html from a named controller or link to another page on the site using any of the $url-methods.
In the Content Templates it is further possible to reference data from the contentslot or from the content itself using: ${slotcontent} and ${slotcontent.content} respectively.

Forms in reactjs with flux

I have a form, this form needs to post some data to my backend. With flux, what is the best practice for doing this, use a store?
My issue with using a store is that I have a sub component inside of my form that allows me to select a number 1-5 with buttons. I wanted that component to be reusable, but if i use a store, I have to hard code the store into the child component which means I cant really use it elsewhere. Instead do I just set the parent state from the child?
If anyone can point out some good tutorials or examples of react/flux forms let me know.
In my opinion any back end interaction should be done by using actions, but...
if you want to use store anyway then you can create additional attribute (prop) in your sub-component which will be a function (f.e. onChange) which should be passed from parent component as prop (this function should set data in store). Then you can reuse this component, because only parent needs to have access to store.
So in subcomponent:
onButtonClick(e) {
this.state.value = e.target.value;
if (this.props.onChange) this.props.onChange(e.target.value);
}
<div>
<button onClick={this.onButtonClick.bind(this)} value="1">1</button>
<button onClick={this.onButtonClick.bind(this)} value="2">2</button>
<button onClick={this.onButtonClick.bind(this)} value="3">3</button>
<button onClick={this.onButtonClick.bind(this)} value="4">4</button>
<button onClick={this.onButtonClick.bind(this)} value="5">5</button>
</div>
and in parent:
setMyStoreState(value) {
store.setNumber(value);
}
<Subcomponent onChange={this.setStoreState.bind(this)} />
or something like this.
Code not tested, but you should get the idea.

how to pass data from view to controller in SAPUI5

I have a view in my sapui5 app, where on a button press I want to pass some data to the controller, to the function invoked on the press event.
Below is the code snippet :
<HBox justifyContent="SpaceAround" alignItems="Center" >
<Input type="Tel" pattern="[0-9]*" inputmode="numeric"
value="{path:'cart>Quantity/value',
type: 'sap.ui.model.type.Integer'}"
class="qtyInput" editable="{cart>Quantity/isEditable}"/>
<core:Icon src="sap-icon://delete" press="deleteItem" visible="{cart>isDeletable}"/>
</HBox>
Here, I need to pass "{cart>lineNumber}" and ”{cart>itemKey}" to the function “deleteItem” which is there in the controller.
Please suggest.
You can try using sapui5 CustomData to pass your custom data on a event.
For that, you need to add below namespace in your view:
xmlns:app="http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1"
and add app:propertyName=“value” inside the Icon element.
Please take a look at below example, I updated your code with the changes required:
<core:Icon src="sap-icon://delete" press="deleteItem" visible="{cart>isDeletable}" app:lineNumber="{cart>lineNumber}" app:itemKey="{cart>itemKey}"/>
Thanks.
Another way to resolve this problem is, if you are getting the data from the same model on which the list is being iterated, you may get the index number of the list item and then read the specific record from the model itself using the index number.
Let me know if you need a code example for this.

Tapestry 5 custom component in form - access during validation

A have problem with accessing my custom components (which are used as parts of the form).
Here is the story:
I have dynamic form which have few modes of work. Each mode can be selected and loaded into form body with AJAX. It looks like that (template):
<t:form t:id = "form">
<p class= "calcModeTitle">
${message:modeLabel}: <select t:id="modeSelect"
t:type="select"
t:model="modesModel"
t:value="selectedMode"
t:blankOption="NEVER"
t:encoder="modeEncoder"
t:zone = "modeZone"
/>
</p>
<div class="horizontal_tab">
<t:errors/>
</div>
<t:zone t:id="modeZone" id="modeZone" t:update="show">
<t:if test="showCompany">
<t:delegate to="block:companyBlock" />
</t:if>
<t:if test="showPersonal">
<t:delegate to="block:personalBlock" />
</t:if>
<t:if test="showMulti">
<t:delegate to="block:multiBlock" />
</t:if>
</t:zone>
<t:block id="companyBlock">
<t:modes.CompanyMode t:id="company"/>
</t:block>
<t:block id="personalBlock">
<t:modes.PersonalMode t:id="personal" />
</t:block>
<t:block id="multiBlock">
<t:modes.MultiMode t:id="multi" />
</t:block>
<div class="horizontal_tab">
<input type="submit" value="${message:submit_label}" class="submitButton thickBtn"/>
</div>
</t:form>
AJAX works pretty well and form changes accordingly the "modeSelect" state. But i run into problem when submitting the form. I have in class definition hooks for components placed as:
//----form elements
#Component(id = "form")
private Form form;
#InjectComponent
private CompanyMode company;
#InjectComponent
private PersonalMode personal;
#InjectComponent
private MultiMode multi;
where *Mode classes are my own components, containing form elements and input components. I planned to get access to them during validation, and check values supplied by user with form, but when I am trying to reach anything from them I've got nullPointerException - it seems that component are not initialized in my class definition of form. On the other hand form component is injected properly (I am able to write some error for example). I am a bit lost now. How to properly inject my components to class page containing the form?
Dynamic forms in tapestry are a bit complicated. Tapestry passes a t:formdata request parameter which contains the serialized form entities. This is then used serverside in the POST to re-hydrate initial form state. This must be kept up-to-date with what the client sees.
If you want to add dynamic content to a form via ajax, you will need to use the FormInjector. You might want to take a look at the source code for the AjaxFormLoop to see an example.
If you want to render hidden form fragments and make them visible based on clientside logic, you can use the FormFragment
From tapestry guide:
A block does not normally render; any component or contents you put
inside a block will not ordinarily be rendered. However, by injecting
the block you have precise control over when and if the content
renders.
Try to use here either "t:if" or "t:delegate".
Something like this:
<t:zone t:id="modeZone" id="modeZone" t:update="show">
<t:delegate to="myBlock" />
</t:zone>
<t:block t:id="companyBlock">
<t:modes.CompanyMode t:id="company"/>
</t:block>
<t:block t:id="personalBlock">
<t:modes.PersonalMode t:id="personal" />
</t:block>
<t:block t:id="multiBlock">
<t:modes.MultiMode t:id="multi" />
</t:block>
java:
#Inject
private Block companyBlock, personalBlock, multiBlock;
public Block getMyBlock(){
if (getShowCompany()) return companyBlock;
if (getShowPersonal()) return personalBlock;
return multiBlock;
}

Listening to click event on ListView

Feels like I'm missing something stupid here, but what's the recommended method to listen to the click event on a listview?
At the moment I've got:
WinJS.Utilities.query(".menuHolder").listen("click", linkClickHandler, false);
And my listview template uses the class 'menuHolder' for it's items:
<div id="menuTemplate"
data-win-control="WinJS.Binding.Template">
<div class="menuHolder">
<!-- menu img -->
<img src="#" data-win-bind="src : pic; alt : title" />
<div class="menuText">
<!-- menu text -->
<h1 data-win-bind="innerText : title"></h1>
<!-- menu desc -->
<h4 data-win-bind="innerText : description"></h4>
</div>
</div>
</div>
I don't seem to hit my breakpoint, in my link handler, or invoke it's function. Any thoughts?
EDIT:
As a follow on question (bearing in mind the item invoked event) is anyone aware of the recommended approach to pass data between a listview and the iteminvoked event, if I say wanted to use the WinJS.Navigator class to move around an application? I'm guessing I need to cast some part of the eventInfo into a suitable object and retrieve information, what part?
Assuming the data you want to "pass" is the data that is bound to the item that was invoked, you can do that in the event arguments that are passed in to the iteminvoked event. One of mine looks like this...
demosLV.oniteminvoked = function(e) {
e.detail.itemPromise.then(function(item) {
var location = format("/pages/{0}/{0}.html", item.data.key);
WinJS.Navigation.navigate(location, item.data);
});
};
So the demosLV is the ListView. I'm setting the oniteminvoked to a function. That function receives "e" as the event args. In the function I access e.detail.itemPromise and hang a .then off of it. Then I access the actual data in the .then using item.data.
Hope that's what you meant. BTW, the format function is one of mine in case you're wondering why it doesn't work for you.
Seems I was being a sausage, I needed to listen for the 'iteminvoked' event on the parent listview id reference, not the child level.
WinJS.Utilities.query("#menu").listen("iteminvoked", linkClickHandler, false);