I have two tabs on my UI.
First tab (default), there is one input textbox and when user submits it, a http call is sent to server
Second tab - Same input textbox + list of checkboxes out of which at least one has to be selected. Then on submit http call is sent.
Both of these cases work fine by themselves.
Now, the problem is - I want my stream to trigger only when form is submitted and not on tab switch. However, I do want the value of tab clicked.
However, currently after the form is submitted once, its being triggered on every tab switch as well ( probably because then it has both selected tab & account available).
Below is the code snippet.
I am not sure what i am missing. Any suggestions are highly appreciated.
detailsByAccount$: Observable<MyType> = this.selectedTab$.pipe(
switchMap((tab) => {
//account$ = account submitted through form
return this.account$.pipe(
switchMap((account) => {
alert(`${account} -------- ${tab}`);
// Server Http call to get the response
if (tab === 'X') {
return this.myService.getDetails(account)
}
return this.myService.getOtherDetails(account);
})
);
}),
);
i would do it like this:
detailsByAccount$: Observable<MyType> = this.account$.pipe(
withLatestFrom(this.selectedTab$),
switchMap(([account, tab]) => {
alert(`${account} -------- ${tab}`);
// Server Http call to get the response
if (tab === 'X') {
return this.myService.getDetails(account)
}
return this.myService.getOtherDetails(account);
})
);
the withLatestFrom operator will not trigger the stream but when account$ is triggered, it will grab the latest value from selectedTab$ and pass it along. It will not trigger though if selectedTab$ has not emitted at least once.
So for this scenario, turns out I had to use BehaviorSubject and it made things little tricky. Below is the answer that I ended up using. Any improvements/optimizations are always appreciated.
Premise of Question -
Two tabs β
Input form.
Input form with a list of checkboxes.
There is a different validator for different tabs. Whenever the form validation passes, form submit is enabled.
Two important things to consider are β Tab has to be selected and the stream should be triggered only after form is submitted.
My first approach was mentioned in the question. However, the issue was http call was being made on every tab switch.
Now, to fix it what I did was β I refactored it so itβs triggered always on account submitted rather than tab selected and then I used take(1) in the inner stream, so it completes itself every time. After I did this this issue got resolved.
detailsByAccount$: Observable<MyType> = this.account$.pipe(
switchMap((account) => {
return this.selectedTab$.pipe(
switchMap((tab) => {
alert(`${account} -------- ${tab}`);
// Server Http call to get the response
if (tab === 'X') {
return this.myService.getDetails(account);
}
return this.myService.getOtherDetails(account);
}),
take(1)
);
})
);
Hope this helps someone. If anyone has any better suggestions, please let me know.
Happy Learning
Vatsal
Related
I am building a web app and have a question to my drop down menu.
The goal is, that the data can only be submitted, when a number is selected in the dropdown menu.
Just check if the dropdown is set or not should be enough. If the input of the request is invalid, the correct response would be a 400.
app.get('/lbc.js', (req, res) => {
if (req.body.dropdown === undefined || req.body.dropdown === null)
res.status(400).send({ message: "Drop down was not selected" });
return;
}
// continue with whatever you want if submit is possible
}
Your frontend needs to handle this response as well, because now it probably won't make a distinction when it works or not. So if you'd capture the submit event of the form, you could send the request yourself using the fetch api, and do different things for a 200 (when the response succeeded) or something like a 400 (request failed).
EDIT:
You can read up on how to create and handle submit events here:
https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event
Here you can read how you can send webrequests through javascript:
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
These are the steps you need to do:
Put an id on your form, e.g. <form id="myForm" ... >
Start a JavaScript tag i.e. <script> </script>
Within these tags, first get the form reference:
const form = document.findElementById("myForm");
Put a submit event listener on it like how they do in the link
In the event handler function, send the request, and deal with the response (show message if error, do something else if success for instance).
EDIT april 7th:
About your code and your comments I have the following to say:
You have created 2 forms, one for your dropdown and one for your button. Both of them should be in the same <form></form> tag. When you click your submit button, all <input> fields will be sent to whereever your form submits to.
You seem to need to check if all fields are filled in correctly before you send the data to your server. Like I said in my previous answer, you need to create a script that will do this. In this link you can read again in a simple way how to do such a thing
Checking if a field has a value is again done by obtaining the element through it's id. E.g. if you have an input like: <input type="text" id="firstName" name="firstName" />, you can obtain the value of this field in javascript as such: const valueFromField = document.getElementById("firstName").value;
Hopefully this will give you some hints to make some new steps.
In my humble opinion it would probably be a good idea for you to do some basic javascript tutorials to give you some insight on how that stuff works.
According to the given details, Try this.
You can check the value and perform your actions as below.
if(req.body && req.body.dropdown) {
res.send('submit is possible')
} else {
res.send('Please select the amount!')
}
Also use, POST method route
app.post('/test', function (req, res) {
res.send('POST request to the homepage')
})
Read more about Express.js routing
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
I am retrofitting credit card tokenization to a legacy application. There is a 5-step registration wizard that takes credit card data on step 4. I need to call the payment gateway's client-side API and want to tokenize the card data before any post-backs occur, i.e. before the next/previous buttons post back. So I added my own click handler to both buttons (showing only the code for the Previous button):
$('#<% = wiz.FindControl("StepNavigationTemplateContainerID").
FindControl("btnPrevious").ClientID%>')
.on('click.PayPageClick', clickEventPayPage);
My clickEventPayPage() event handler gets called correctly and does its thing, i.e. the tokenization request returns successfully and the card number in the form is replaced with the token.
var clickEventPayPage = function () {
if ([tokenization has not occurred yet]) {
clickSource = this;
requestToken([...], submitWhenDone);
return true;
}
else
return true;
}
}
I tried different combinations of returning true and false from my event handler in the hope that the ASP.NET event handler gets called after mine, but that does not appear to happen.
The API then calls my submitWhenDone function
function submitWhenDone(response) {
// do various cleanup and then post back to server
__doPostBack(clickSource.id,'postPayPage');
}
The problem is that __doPostBack() does a post-back, but it does not advance the wizard to the next or previous step. What is the correct way of doing this? I just want to avoid rewriting the wizard if I can.
Ok, this helped.
function submitWhenDone(response) {
// do various cleanup and then post back to server
clickSource.click();
}
and my event handler has to return false to prevent double-posting.
Currently I am using a ajax post back to pass data to a my controllers, wherein I do my magic from there. The problem I am having right now is that we have removed a submit button and instead attached a handler using .keyup(), on a smaller database this works great! However when moving it to a larger database, as you might of guessed, it causes all sorts of issues, from delayed responses to crashes etc. So it won't work. My question to stack overflow would but this: What's a user-friendly version to submit a form?
Since the front end user could potentially be searching for single character values (e.g 6 as in userid 6) I can't limit it to a minimum character submission. We don't want a delay timer as that could back fire in a couple different ways. so basically I've ruled out .delay(), .blur(), and .keyup().
If your requirement is to not use a submit button but rather process the input as the user types then I have used solutions like this in the past.
First define a function to manage the delay, something like this:
var inputDelay = (function () {
var timer = 0;
return function (callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
Then for your actual ajax-call do something like this in your onkeyup event:
inputDelay(function () {
//AJAX-call goes here
}, 400)
Please note that this is not a universal solution for handling the order of responses from the server though. You might very well get several AJAX requests sent to the server and this code does nothing to make sure you only handle the latest call.
The best way to handle your situation would be to use a form, complete with a submit button. You would then add your event listener to the submit event of the form, cancel the post-back, and then do your processing. This would handle both the user clicking the submit button as well as pressing the enter key from inside the form.
I had the same issue as you.. What I did is the following:
I used on keyup.. and I fire the search if the user stayed still for .. say 300 millisecond or 500 millisecond..
While the user is typing I clear and reset the setTimeout().. so if the user stayed still for the timeout time the search will fire.
Additionally you can take the reference of the xHR object and cancel it if another request was to take place..
Do I understand your question correctly? if you need a written example I can write you a fiddle.
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.