Inside my app.component , I have a background mode service which when shared via intent throws value to a behaviour Subject
this._notification.setNotiService2(data.extras);
Once logged In , I am setting the root to TabsPage
this.appCtrl.getRootNav().setRoot('TabsPage');
On Tabs Page , I have subscribed to the behaviour subject , so whenever I get a shared object , I process it and open a Modal displaying the required values.
Initally when the app opens , everything works fine. But once we login/logout the problem occurs. On logging out , I am setting the root page as Login Page.
this.appCtrl.getRootNav().setRoot('LoginPage');
Then again on successful login setting root to Tabs Page
this.appCtrl.getRootNav().setRoot('TabsPage');
Now again if I share the values via intent multiple instances of the Modal are opening with the exact same values.
I have checked for behaviour subject as being null/undefined but the subscribed value is Ok only . Logging the value from behaviour Subject inside the TabsPage , I found the same function (subscribed behaviour subject) is being called twice.
Again if I logout/login the Modal opens 3 times and the number continues to grow accordingly.
It sounds to me, that you are not remembering to unsubscribe, which means that the subscriptions increment each time. So whenever you leave a page, remember to unsubscribe to (all) your subscriptions. Since you are using Ionic, the ionViewWillLeave hook would be a suitable place to unsubscribe... so declare a new Subscription on your page and...
import { Subscription } from 'rxjs/Subscription';
// ...
mySubscription = new Subscription();
// ...
this.mySubscription = this.myService.mySubject.subscribe(....)
// ...
ionViewWillLeave() {
this.mySubscription.unsubscribe();
}
Related
I'm trying to build an OAuth2 sign in flow in my React app using Google as my Auth provider.
To do this, when a user hits 'login',I open up a new window and direct the user to Google to sign in. (I use this npm package https://www.npmjs.com/package/react-new-window to do so)
I want to close this pop up window when a user has signed in successfully, to check for a successful sign in I want to keep track of the URL in the pop up window (which will change to a redirect URL indicating login success). I keep track of the New Window by assigning a ref to it from the parent component.
var newWindowRef = React.createRef();
return (
<NewWindow
ref={newWindowRef}
url="http://localhost:8080/oauth2/authorization/google"
>
</NewWindow>
)
How can I keep track of the URL in the new Window?
https://www.gethugames.in/2012/04/authentication-and-authorization-for-google-apis-in-javascript-popup-window-tutorial.html -> this post suggests using an interval timer, but this seems crude.
I've also tried using a hook in the parent component:
useEffect(() => {
console.log("New Window URL Changed");
console.log(newWindowRef.current.window.document.URL);
}, [newWindowRef.current.window.document.URL]);
The problem here is that newWindowRef.current is null when the parent component is first rendered, and this errors out: "TypeError: newWindowRef.current is null"
I am trying to interact between 2 components i.e. the header component and the router-outlet component which contains a reactive form. Some days ago after great research, I got an answer which told me to use shared service and there takes a ReplaySubject variable. This will act as an Observable which will be called in ngOnInit of the component in router-outlet. So the problem arises here. Everything is working fine but just now while testing noticed a small bug. After clicking the button in header the event is firing in the main component but it is staying active until I hard reload the page.
Below is my code:
shared.service.ts
public updateSrc = new ReplaySubject<any>(1);
public updateClick = this.updateSrc.asObservable();
header.component.ts (A button is clicked here)
update() {
return this.dataService.updateSrc.next(true);
}
main.component.ts (Here the main operation is happening on click of header button)
updateClicked = new Subject<any>();
ngOnInit() {
this.dataService.updateClick.pipe(takeUntil(this.updateClicked)).subscribe({
next: value => console.log(value); // At first click of button this happening perfectly but if coming from different route & had clicked the header button previously this thing is getting triggered
});
}
ngOnDestroy() {
this.updateClicked.next(); // Closing subscription here
}
Can anyone suggest to me how to reset this?
The ReplaySubject (and the BehaviorSubject) stores previously emitted values and emit those whenever a new subscriber is added.
If you don't want new subscribers to receive previously emitted value(s), you can always use Subject which doesn't store emitted values.
You may want to read more about the available variations in the docs.
It should be noted that I am using Redux with React-Native.
For simplicity sake, lets say I have an email viewer. There's a homepage with a massive list of emails and when you tap on an email, you are navigated to another screen. Here, the contents of the email, title, subject, body, is all held in a piece of state managed by redux. This all works fine.
Lets say I back out of that page, and now navigate to another email. The previous email pops up for a split second before the new email is shown. It should be noted here that I am storing the data in AsyncStorage as a sort of "cache?". The issue here is that since I only re-update the state whenever I tap on an email, the state which is the body of the email viewing page gets updated a split second after the user is navigated to it. This, is annoying.
The heart of the question is this
How can I store the body of my data in another piece of state, functionally identical to the current-email-viewing-state without overwriting the currently active state?
or
is this even the best way to do this?
Thanks
You could use Redux's lifecycle methods to handle this. Let's say the state for your email detail component looks something like this:
export const initialState: StateShape = {
loading: false,
readOnlyEmailData: {
recipientEmails: null,
senderEmail: null,
body: null,
},
};
When the email detail component (let's call it EmailDetail.jsx) is loading, you can use ComponentDidMount() to get and set your values.
You'll probably have actions and actionCreators like getEmail, getEmailSuccess, and getEmailError. Set loading to true in getEmail, and then false again on success or error. You can add a conditionally rendered spinner component (easy to borrow from something like MaterialUI) to EmailDetail, which is visible only when loading is true, and render the rest of your content when loading is false.
When the user hits the back button or otherwise navigates away from the component, componentWillUnmount() can be given a list of things to do as the component prepares to unmount. In this case you could use a reset method to reset loading and readOnlyEmailData to initial state on unmount.
When the user clicks on a new email, it will load the new email data on mount and show the spinner until the new email data is available.
There are other ways to do this, this is not the most optimized, but it should work quite a bit better than what you've tried so far. I hope it helps :)
I currently have a router with a path set like so:
{path: "application/:id", component: ApplicationComponent}
I generate a menu of links by using an ngFor loop. it uses the index to generate a dynamic routerLink of:
routerLink="application/{{index}}"
Once clicked, it travels to the correct URL and uses my ApplicationComponent's ngOnInit method to grab the index in the url and pull a specific object out of an array in a separate class.
HOWEVER it only works for the first link you click on. If I click on link 1, it loads the page correctly with object 1's data. But when I click link 2, it travels to application/2 in the url but keeps object 1s info up. If i refresh the page and click link 2, object 2's info is shown, so I know it is pulling from the array correctly.
I suspect that once any links are clicked the ApplicationComponent uses the ngOnInit method to make the component, and then if another is clicked, this method is not called so the variables are not getting updated.
How can I solve this problem? I need the Application Component to know that ive traveled to a new sublink so it can call ngOnInit again
ngOnInit is only called once, when the component is loaded. So if you navigate to application/1, ngOnInit is called. If you then navigate to application/2, ngOnInit is not called again because the component is already loaded. You need to use something like paramMap from ActivatedRoute to detect when a parameter changes.
ngOnInit(){
this.activatedRoute.paramMap.subscribe(paramMap => {
let id = paramMap.get('id'); // id gets updated whenever parameters change
// add or call any code that needs to re-run when a parameter changes here
});
}
I have an application which searches for flights using Vue.js and Vue Router.
I have two components, first one is search, which is on the base route '/'. When user clicks on search, it will send a request to server and gets a huge list of flights.
Then I need to call the result component on '/results' route and show the results using v-for.
I have two questions, first, how can I manually redirect to '/results' after I get the results.
Second and more important, what is the proper way of passing the results data to results component to use?
Inside your results components, you can put transition hooks in the route object. Read here: http://vuejs.github.io/vue-router/en/pipeline/hooks.html
The activate hook runs when a component is activated, and the component wont appear until the activate hook has run. Here's the example on that page, which would be similar to yours:
route:{
activate: function (transition) {
return messageService
.fetch(transition.to.params.messageId)
.then((message) => {
// set the data once it arrives.
// the component will not display until this
// is done.
this.message = message
})
}
}
So basically when they click search you send them to /results and this hook will handle loading the data in between.
Here's an advanced example of a mail app using vue-router that shows off a lot of the transition hooks in action: https://github.com/vuejs/vue-router/tree/dev/example/advanced