I built a form using react,react-query,
link to the code
I built custom fields:
CacheAutocompleteField - cache field using react-query
queryAsyncFunc props - get async function and cache the data using react-query
I have 3 fields:
Type - Select field
Country - CacheAutocompleteField
City - CacheAutocompleteField
My scenario:
I select any type from my hardcoded list (Type A, Type B , Type C),
I search any country, then I search any city
What I'm trying to do?
every time I select a new type (from type options) - I want the country and city fields to be reset.
every time I already search the same key (the queryKey is combined of cacheKey+inputValue) , it will not call to api, it will get the results from cache (that's what I chose to use from react-query to do).
What I'm getting when I run my code?
When I select a type A, enter a country “Island” it will fetch data from api and get the data.
Then when I select a type B, enter a country “Island” - It will fetch data from api and get the data.
But when I select a type A and and same country “Island” again - I don’t want it to fetch data from api - I want it to get data from cache (that’s the reason I chose to work with react-query too) because it already search for this data with the same type. The queryKey is depended of other type field.
when I search anything from autocomplete and it not find it, then I try to reset it by select any other type, it will kind of reset the value of the input but it still exist in inputValue of the country.
for example I select type C, then enter "lakd" in country, then select any other type, it not reset it. reset works for me only when anything find in autocomplete and I select it. I guess it's because the autocomplete component not have inputValue props, but when I use it it make me other issues.
You needn't call refetch. It call the API regardless of the cache.
Comment/Remove this code
// useEffect(() => {
// if (debounceInputValue.length > 1) {
// refetch();
// }
// }, [debounceInputValue, refetch]);
And you should enable the useQuery
enabled: true,
And use debounceInputValue instead of inputValue for useQueryData
https://codesandbox.io/s/react-query-autocomplete-forked-d84rf4?file=/src/components/FormFields/CacheAutocompleteField.tsx:1255-1263
Related
I'm trying to set dynamically filters from JS using Power BI SDK.
When I check on Power BI service the filters to "select all"
then in my JS code get filters as bellow
rapport.getFilters().then(data => {
console.log("filters data", data)
....
I'm getting an empty array :
Secondly when I try to set filter using setFilters API nothing happens
report.setFilters(filters)
Here's what I send in 'filters'
My code works only in one case:
When I go to power bi service and uncheck selectAll then check manually one by one the filters.
In this case , when I setFilters the embedded view is getting updated.
But in my case I'm generating reports automatically so I can't go to PBI online and check every filter, I should keep default "select all" checked and set filter from front-end.
Any solution to set filters programmatically with default checked "selected all " ?
The response you are getting by using getFilters() seems correct.
To set filters programmatically with default checked as "Select all", Please use below code.
// Create a filter
const filter = {
$schema: "http://powerbi.com/product/schema#basic",
target: {
table: "<TABLE-NAME>",
column: "<COLUMN-NAME>"
},
operator: "All",
values: []
};
// It is recommended to use the updateFilters API to apply filters
report.updateFilters(models.FiltersOperations.Add, [filter]);
For more info about creating and applying filters, refer the docs
I intend to use Autocomplete such that it stores a certain property of an object in the form state and displays a different property in the autocomplete option list. For example, if the option list is as follows:
[
{ gender_name_short: "F", gender_name_long: "Female" },
{ gender_name_short: "M", gender_name_long: "Male" },
{ gender_name_short: "O", gender_name_long: "Other" }
]
I intend to store gender_name_short in the form state and display gender_name_long in the dropdown list. I was able to achieve this and here is the codesandbox link for the implementation I did (Can you also suggest a better way?)
https://codesandbox.io/s/cool-bogdan-6lyxs?file=/src/App.js:231-412
Now my issue is that I want to also be able to store multiple values in an array,not applicable in this example as when person cannot have multiple genders, but I would like to use it for other use cases. So how should I go about doing that
Current Behavior 😯
If I add the 'multiple' prop to the Autocomplete component i get the error
TypeError
Cannot read property 'filter' of undefined
Expected Behavior 🤔
stores the multiple values in an array in its respective short_name_format
eg) ['M','F','O']
Steps to Reproduce 🕹
https://codesandbox.io/s/cool-bogdan-6lyxs?file=/src/App.js:231-412
Steps:
Currently the code works without multiple to give a demo of what works
Un-comment multiple in App.js in Autocomplete call (prop)
Change the initialisation of gender to an empty list
This sandbox shows a possible solution to your problem.
Checkout the transformValue util I've added at the top of FormikAutocomplete.js:
const tranformValue = (value, fieldtosave) =>
Array.isArray(value)
? value.map(v => v[fieldtosave] || v)
: value[fieldtosave];
And its usage:
<Autocomplete
onChange={(_, value) =>
setFieldValue(name, value ? tranformValue(value, fieldtosave) : null)
}
...
/>
Basically, what you were missing is that when the Autocomplete is set to multiple the onChange value is an array.
I am trying to use and learn Strapi Headless CMS while implementing it in a small company. I need to calculate some fields and display it in the form (while filling fields) and table.
I was looking in the model life cycle but I did not find any cicle regarding input changes, just the model.
I have tried beforeSave cycle but it is obviously triggered after an user clicks on Save button, but according to their own documentation should work:
beforeSave: async (model, attrs, options) => {
model.set('FinalCost', attrs.budget- attrs.cost);
}
This code doesn't work, but I am trying to show how the finalCost field, after filling cost and budget should look like (in real time). I also tried attrs.FinalCost = attrs.budget - attrs.cost but nothing changes.
Any clues? Thanks in advance.
Edit:
I had to verify that budget field were truthy before setting FinalCost:
beforeSave: async (model, attrs, options) => {
if (attrs.FinalCost) {
attrs.FinalCost = attrs.budget- attrs.cost;
}
}
But this does not answer my first issue, that this should work in real time and bot until I press "Save" button.
Here are some resources that will help you.
Model's life cycles function are called when an entry is created/update/...
So in your case FinalCost is an attribute of your model and its value will be updated and saved any times your update your entry.
This is the same system as in this guide - https://docs-v3.strapi.io/developer-docs/latest/guides/slug.html
If you don't want to store the value is a field, you will have to update the API controller to calculate the value on the fly.
That is done in this guide - https://docs-v3.strapi.io/developer-docs/latest/guides/custom-data-response.html
The use case (tl;dr):
On a search page, the user picks 0 or more 'markets' from a dropdown. The search results then filter to only show results which contain at least one of those 'markets'. Alternatively, the user can visit a URL with '?markets=A,B,C' query parameters set, to see results filtered to those markets. Of course, when the user picks options from the dropdown, the URL must be altered accordingly, so that they can bookmark/share that URL to share that view with someone. Herein lies the issue - we want the URL to update when the dropdown does, and the dropdown to update when the URL does, creating a cyclic dependency. How can we implement this such as to not set off an infinite loop when one is changed, as I am currently experiencing?
Technical implementation:
There are two templates: search_page and search_filters. The latter is a component included in the former.
In search_page, there is a helper searchFilterArgs(), which reactively gets the value of 'markets' from the query params (using universe:reactive-queries), and sets the data context for search_filters accordingly. It also passes a setFilters callback to the component. When the component calls the callback, search_page sets the workflow parameter in the URL to the passed value.
Template.search_page.helpers({
searchFilterArgs(){
const instance = Template.instance();
const splitOrEmptyArray = function(filter_key) {
if (UniUtils.url.getQuery(filter_key)){
return UniUtils.url.getQuery(filter_key, false).split(',');
} else {
return [];
}
};
return {
setFilters: instance.setFilters,
//get each query parameter as a non-reactive source, '' if not set, then convert to array
selectedMarkets: splitOrEmptyArray('markets'),
}
},
...
});
Template.search_page.onCreated(function() {
this.setFilters = (key, value) => {
if (UniUtils.url.getQuery(key, true) !== value){
UniUtils.url.setQuery(key, value);
}
};
});
In the search_filters component, there is an autorun on Template.currentData() that sets the dropdown to the 'markets' value in the data context. There is also an 'onChange' handler for the dropdown that calls the 'setFilter' callback with the value when the dropdown is changed.
Observed behaviour:
When the page is loaded, workflows is initialized to be empty. Then, I choose a market ("Sales") from the dropdown. The page then enters an infinite loop of changing the URL and changing the dropdown. Upon setting breakpoints, it was revealed that after I changed the dropdown value, the callback fired with the correct value, and the URL query parameters were set correctly. Then the component's data context is changed, which somehow causes the dropdown onChange handler to fire, but with the value "", rather than "Sales".
Workaround:
For now, I've changed the searchFilterArgs helper to retrieve the URL query parameters in a non-reactive way. Hence, when the page is first visited with a certain URL, the market parameter is passed in to the search_filters component. After this, any time the dropdown in the component is changed, the callback is fired, and the URL changed. Since the URL is retrieved non-reactively, this doesn't trigger a consequent change in the dropdown. It works fine for our previous use case, where the URL would only be set when opening the page, and not changed by the user after that. However, we now have code that will change the URL parameters on behalf of the user, and the filters should update accordingly.
Do you have experience with circular dependencies like this in Meteor? Is there a pattern to tackle this problem?
(I'm using Meteor 1.3.2 and Semantic-UI for the dropdown).
I have a dijit Select widget and need to do something when the user clicks one of the dropdown items. Meaning I need access to the clicked item, to retrive some information, and call one of my own functions.
I've tested to attach an onChange on the select and I can get the text value selected fine. But I need the object and not the value. The object holds more values in a data-info-attribute.
Basically what I'm trying to achieve is to show one value in the list but send along more values to populate other fields when selected.
Background: This is a typeahead field populated thru AJAX by a server function. There IS a store attached but it's empty (as far as I can tell) so I've been unsuccessful trying with: .store.fetchItemByIdentity - always returns nothing.
ta.store.fetchItemByIdentity({
identity: ta.getValue(),
onItem: function(item, request){
console.log(item),
console.log(request)
}
})
I expect the log to show item- and request-object, but they're both undefined.
ta.getValue() get's the selected value as expected.
What's the best way to achieve this?
Have a look at my answer to onChange not sufficient to trigger query from Dojo Combobox and also to jsFiddle mentioned there. I added code specific for your needs there:
select.dropDown.on("itemClick", function(dijit, event) {
var node = dijit.domNode;
console.log(domAttr.get(node, "data-info-attribute"));
// or
console.log(node.dataset.infoAttribute);
});