Ajax calls for combobox not working
I am trying to fill a dropdown from server responses and noted that input type: ajax does not do in my interview as I expected (I am on v1.3.50 currently).
I noticed also in the documentation it does not work - is something broken with this functionality?
By default, the Ajax input requires you to type at least 4 characters. You can adjust that by setting trigger at: 2 (the smallest trigger).
It works fine in the documentation link you shared if you type at least 4 characters.
Then I might need some help to find the suitable way please - here is what I am trying to do: I have dropdown that I want to populate with data from a server (API). The dropdown actually is a list of ~100 company names from which the user should be able to pick one.
However, response time is rather high (approx. 5s). Hence I would want the question to show with the dropdown being populated (via ajax) after loading the screen...
As input type: ajax requires at least 2 characters to be typed, it doesn't help here...
This is roughly what the question currently looks like, but given the synchronous call to the API function it takes a while to load...
---
question: |
Select affiliate
subquestion: |
Select an Affiliate from the dropdown below.
fields:
- Affiliate ID: company.id
code: |
server_getaffiliates()
#This actually is a call to the API (py module) which gets the data from the server
The JavaScript behind datatype: ajax has some problems, so I'll need to work on that. It works sometimes.
Anyway, datatype: ajax is designed for long lists, not for lists that take a long time to obtain.
To load the options of a dropdown after the screen has loaded, you could use action_call() along with some jQuery:
mandatory: True
question: |
Your favorite fruit is ${ favorite_fruit }.
---
question: What is your favorite fruit?
fields:
- Fruit: favorite_fruit
choices:
- Please wait
script: |
<script>
action_call('fetch', {}, function(data){
var elem = getField('favorite_fruit');
$(elem).empty();
for (var i = 0; i < data.length; ++i){
$(elem).append($("<option>").val(data[i]).text(data[i]));
}
})
</script>
---
event: fetch
code: |
# Your code that calls the slow API would go here.
json_response(["Apple", "Orange", "Peach", "Pear"])
Another way you could handle this problem is by keeping an in-memory cache of the list of companies in Redis. You could do something like this:
modules:
- .loadlist
---
mandatory: True
code: |
cache_refreshed
welcome_screen
final_screen
---
code: |
background_action('bg_cache_refresh')
cache_refreshed = True
---
event: bg_cache_refresh
code: |
# ensure that the list stored in the Redis cache is current
get_fruit_list(refresh=True)
# return from the background task
background_response()
# nothing is returned because the purpose was just to put
# some data in Redis, to be retrieved later
---
question: |
Welcome
subquestion: |
I am just stalling for time because there is an API call running in the
background.
continue button field: welcome_screen
---
question: What is your favorite fruit?
fields:
- Fruit: favorite_fruit
code: get_fruit_list()
---
event: final_screen
question: |
Your favorite fruit is ${ favorite_fruit }.
where loadlist.py is:
from docassemble.base.util import DARedis
# Using the time package is just for purposes of simulating
# a long-running API call
import time
__all__ = ['get_fruit_list']
def get_fruit_list(refresh=False):
r = DARedis()
fruitlist = None if refresh else r.get_data('fruitlist')
# if the list is not in the cache, or the cache should be
# refreshed, call the API
if fruitlist is None:
# fruitlist would actually set by an API call; this is just
# a demonstration
fruitlist = ["Apple", "Orange", "Peach", "Pear"]
# simulate a slow API call
time.sleep(5)
# store the list in a cache for five minutes
r.set_data('fruitlist', fruitlist, expire=60*5)
return fruitlist
In this interview, a background task is launched in advance, before the user sees the screen with the list of companies (fruit in this example). This background task will fetch the list of companies from the API and store the results in Redis. Then when the user gets to the screen with the list of the companies, the function get_fruit_list() will return the list of companies from the cache, with no five-second delay.
The list will be stored in the Redis cache for no more than five minutes. If the user takes more than five minutes to get to the screen with the list of companies, the list will need to be fetched by the web application and the user will need to look at a spinner, but that is probably unlikely to happen.