select drop downs and promises - briefly empty selects - javascript

I have two filters where the first influences the second before querying data to a remote server.
I have a select, which has its options in $scope.locationCodes. This select relies on another dropdown (customer). Once the customer is selected, I have the ID, and then pass that to the returned promise in the first function.
The issue 1:
As soon as I select a customer, if I really quickly go to select a location code (in codes dropdown), the options will be blank. If i blur and then try to select again, the options will show up, indicating there was a bit of a delay. What's the best way to handle this delay in the array being populated?
The code:
HTML:
<select data-ng-options="locationCode in locationCodes"></select>
JS:
$scope.getShipAbbleLocations = function (terminalId) {
var customerId = $scope.customer.id;
return Service.getShipAbbleLocations(customerId, terminalId).then(function (data) {
getLocationCodes(data);
_.defer(function () {
$scope.$apply();
})
});
};
function getLocationCodes(data) {
for (var i = 0; i < data.locationCodes.length; i++) {
$scope.locationCodes.push(["(" + data.locationCodes[i].shipThruLocationCode + ")", data.locationCodes[i].shipThruLocationId]);
}
$scope.$apply();
}
The issue 2:
The kicker is that the codes dropdown should be disabled until a customer is selected. I have been relying on a timeout in my controller to handle the disable by setting a delay for the 'enable' which allows time for the array to be populated. This works, but the issue re appears once you change customers and don't have to worry about the initial disable case.
I am wondering if there is a better way than timeouts (that hopefully shows a better understanding of angularjs/promises) to handle this delay/asynchronicity.

The best way is to show LOADER and disable the UI until your AJAX call is complete.
It is always a better practice to implement loader for async ajax calls.
You can implement the loader code in interceptors, so that for every request the loader will show up and you dont have to implement for every AJAX call, since its in the interceptor.

Related

Vue Search Inputs fires Fetch-Method every single keypressed [duplicate]

background
I have a number of dropdowns on a page. If you change the first one, the rest of the dropdowns are updated according to what you've selected.
In our case, we deal with fund data. So the first dropdown is "All Fund Types". You select "Hedge Funds", and the next dropdown is filtered by options that only apply to Hedge Funds.
The client is now asking me to put a text field into the mix, which when the user starts typing, will effect those results.
So if they type "USD", the second dropdown will only contain options that have funds with "USD" in the name.
problem
The specific problem that I'm having is that with the code I'm using:
$('#Search').keypress(function () {
// trigger the updating process
});
It's triggering the search for each key press. So when I type "USD" I'm getting 3 requests immediately - one for "U", one for "US" and one for "USD".
I've tried setting a timeout with this:
$('#Search').keypress(function () {
// I figure 2 seconds is enough to type something meaningful
setTimeout(getFilteredResultCount(), 2000);
});
but all that does is wait 2 seconds before doing what I've described.
I'm sure this problem has been solved before. Could somebody suggest how to solve this issue?
The way I have done this before is to set a timeout, but clear the existing timeout each time a new key is pressed. That way you should only get the request being sent when the user has stopped typing.
var timeoutId = 0;
$('#Search').keypress(function () {
clearTimeout(timeoutId); // doesn't matter if it's 0
timeoutId = setTimeout(getFilteredResultCount, 500);
// Note: when passing a function to setTimeout, just pass the function name.
// If you call the function, like: getFilteredResultCount(), it will execute immediately.
});
(I'd go for about 500ms timeout.)

In Angular 5 , How should group number of click events and emit once only when user stop clicking

I have a angular application (Angular5 + ngrx5).
In my app, users can click/select are number of options/items (just simple divs). such as option1, option2, option3 .... Multiple options can be selected.
Currently, users select one option, we trigger a service call to save a selected option. We do currency version number checks, so users have to wait the service call response (with latest version number) back before select the next option. It is not providing good UX.
Therefore, I prefer that users can keep clicking a number of options, once users stop clicking we make a single service call with multiple selections .
My question is that how to group number of click events and emit once only when user stop clicking? any examples or any suggestions?
I know rxjs debounce can drop emitted values.clicks, but I do not want to drop them I want to aggregate them in order to make a single service call.
thank you
The rxjs way of achieving this.
const ajax=Rx.Observable.timer(4000).mapTo('ajaxcall')
Rx.Observable
.fromEvent(click, 'click')
.scan((acc,curr)=> ([curr,...acc]) ,[])
.debounceTime(500)
.mergeMap(arrClick=>{
console.log(arrClick)
return ajax
}).subscribe()
https://jsfiddle.net/7kbg4q2e/453/
You can handle this with a setTimeout.
On each click, you can build an array with your values.
Then, set a timer to send to the API. If it receives another request while waiting for that timer, cancel it, and start a new one.
handleClick(value) {
this.values.push(value);
// If there's a timer going, cancel it
if (this.timer != null) {
clearTimeout(this.timer);
}
// Start a new one
this.timer = setTimeout(() => {
// Send to your API
// Make sure to clear your this.values array after the API call has returned
}, 5000);
}
In the example above, it will only send the request after 5 seconds without a click event

How to use $watch to see changes when updating a database record

I'm having nightmares on how to display on the UI what I have changed in my database. I have this scenario that I need to select certain titles and then I will click a button that will change its status.
Problem is when I select titles and then I click the change status button it don't automatically reflect on the UI. Here is my update function.
$scope.updateTitleStatus = function(statusId, cp){
ContentAssessmentService.updateSelectedTitles($scope.selectedTitles, statusId);
$scope.selAll = !$scope.selAll;
$scope.selectedTitles = [];
};
Here is my service.
this.updateSelectedTitles = function(selectedTitle, statusId){
var self = this;
_.forEach(selectedTitle, function(selectedTitle){
ContentAssessmentFactory.updateSelectedTitleStatus(selectedTitle.id, statusId);
});
};
Here is my array which is the selected title stored.
$scope.selectedTitles = [];
Can you tell me how to use $watch function? I don't know how to do it. I've done this but it doesn't work.
$scope.$watch(function($scope){
return $scope.selectedTitles;
}, function(newValue) {
$scope.selectedTitles = newValue;
console.log(newValue);
});
I just need to update the UI immediately without refreshing the page (that's my last option but trying not to) when I have click the change status button.
You are going to have to use polling or a websocket connection. $watch does not "watch" your database. It watches #scope variables that are usually bound to the view and reacts to changes there. It sounds like you are looking for something more like meteor.js that keeps an open websocket and will dynamically update the view when the database is changed from another client, background process etc. These are completely different things. To achieve this sort of behavior with angular, the easiest approach would be to poll your api incrementally and update models in angular when the api gives you modified data.

select2 - get current search text

I'm using select2 and fetching available options from the server with this query function
var recipientsTimeout;
this.loadRecipients = function (query) {
clearTimeout(recipientsTimeout);
recipientsTimeout = setTimeout(function(){
data.transmitRequest('lookupcontacts', { search: query.term }, function(resp){
query.callback({ results: resp.items });
});
}, 500);
};
It uses our own ajax layer, and delays searching until the user stops typing, and it works fine. The only small issue is that if the user types some text, then immediately backspaces over it, my last timeout will still fire, and an ajax request will be fired, and ignored. This doesn't cause any problems, but it's less than optimal.
Is there any (preferably non-brittle) way to fetch whatever the current text is? It seems as though the query object sent in has an element property, which is a jQuery wrapper of the original hidden input I set select2 up with, but there's no direct way I can see to get the actual textbox that the user is typing in. Obviously I could inspect it and easily figure out the dom pattern and build up a selector from the hidden element back to what the user is typing in, but I really hate doing that, since the specific layout could easily change in a future version.
So what is the best way to get the currently entered text, so I could do a quick check on it when the setTimeout expires, and I'm about to run my ajax request.
I'm using 4.0.3 and the way I did it is this:
$("#mySelect2").data("select2").dropdown.$search.val()
The hidden input element (where you initialize select2 on) gets a data property select2, which contains references to all elements, that are used by select2. So you could do something like this:
var hiddenInputSelector = '#e1',
select2 = $(hiddenInputSelector).data('select2'),
searchInput = select2.search;
if($(searchInput).val() === '')
clearTimeout(recipientsTimeout);
This is not in the docs though, so it might change in the future.
In select2 version 4.0.3 this works for me, whereas the others did not:
$('.select2-search__field')[0].value;
I do something like this to get the current search term:
list.data("select2").search[0].value;

Request to Asp page misses if called very fast

I have a website with is made in classic asp. On one page there are 5 textboxes and 5 labels. When users type some value in the textbox then using javascript (added on Onchange event) another asp page is called which done some calculations, fetches some database values based on the value typed and return the result to caller page to be displayed in the corresponding label. Same Page is called for all the textboxes for fetching result. My problem is if user types at normal speed and move to other textbox(using tab) at a normal speed the second page is called properly and result displayed properly in labels, but if the user type very fast then the request for the value of one textbox is not completed and other is send and as a result there is no result value coming for first one. Is there is any way to handle this.
Thanks
If you don't want to refactor the whole thing to use XHRequests, there's a simple solution. Have a "request in progress" flag that is checked in your onchange event. If a request is in progress, queue the current action. In the result display code, check the queue and execute the next action, if any. If there are no remaining actions, reset the flag.
Here's a basic outline in code:
var requestInProgress = false;
var pendingActions = [];
function OnChangeHandler(onChangeFunction) {
if(requestInProgress) {
pendingActions.push(onChangeFunction);
}
else {
requestInProgress = true;
onChangeFunction();
}
}
function ResponseHandler()
{
//Your regular response handling code
if(pendingActions.length > 0) {
var nextFunction = pendingActions.shift();
nextFunction();
}
else {
requestInProgress = false;
}
}
I'm no JS expert, so I'm sure that this could be cleaned up a little bit, but it should handle overlapping events just fine.

Categories

Resources