Next() is not being called on beforeRouteUpdate using the same component - javascript

I am using the same vue-component to display data that I'm fetching with axios. After clicking on a link the ajax-request is fetching new data.
But if the component is already loaded and the user clicks another link to load different data, the next()-method is not called.
Check out my codepen to see what I mean. There should be an alert showing. If you click a link for the first time, it does. If you switch to another link, it does not.
https://codepen.io/spqrinc/pen/YzXGGGL

From the docs:
Note that beforeRouteEnter is the only guard that supports passing a callback to next. For beforeRouteUpdate and beforeRouteLeave, this is already available, so passing a callback is unnecessary and therefore not supported
So the next() in beforeRouteUpdate can't take a callback because you already have access to the instance in this. Anything you would have done with vm in that callback can be done without it using this:
beforeRouteUpdate(to, from, next) {
alert("Reloaded");
// `this` works
next();
}

Related

Ionic event to identify user came from navCtrl.pop()

I have three pages named as page1,page2 and page3
and their navigation will be page1->page2->page3
How we can identify on page2 that user came on this page via NavController Push or Pop function.
I just want to trigger a function on page2 only if they perform some action on page3 and redirected to page2 via navCtrl.pop() function.
I know i can put condition in ionViewWillEnter for last view name from nav stack, Because it will be fired everytime user be on page2. But If there is any other alternate or good way to do this. Please let me know
Thanks
Identifying if we came to a page using push is easy as it is the standard way of passing a parameter from parent to child in navCtrl.push(PageName, parameters)
I think what you are asking is how to do the opposite: pass a value back from child to parent.
Below is a solution for this (see this link for details):
In parent, instead of the normal push function, create a new promise, and in the then part, you can get a parameter back from the child.
In child, after pop() call its then, and pass a parameter through the resolve of the promise that you initially created to the parent.
Parent
new Promise((resolve, reject) => {
this.nav.push(ChildPage, {resolve: resolve});
}).then(data => {
// process data
});
Child
this.nav.pop().then(() => this.params.get('resolve')('some data'))
I have used the above solution many times. There is also this alternative solution, but I haven't tested it myself:
https://forum.ionicframework.com/t/solved-ionic-navcontroller-pop-with-params/58104/3
I'm using globalThis['datatoinput'] to input my data and then using ionViewDidEnter on my parent page to read globalThis['datatoinput']

Angular4 - how to ensure ngOnDestroy finishes before navigating away

I have a list of objects. The user can click on one, which then loads a child component to edit that component.
The problem I have is that when the user goes back to the list component, the child component has to do some cleanup in the ngOnDestroy method - which requires making a call to the server to do a final 'patch' of the object. Sometimes this processing can be a bit slow.
Of course what happens is the user arrives back on the list, and that api call completes before the database transaction from the ngOnDestroy completes, and thus the user sees stale data.
ngOnDestroy(){
this.destroy$.next();
this.template.template_items.forEach((item, index) => {
// mark uncompleted items for deletion
if (!item.is_completed) {
this.template.template_items[index]['_destroy'] = true;
};
});
// NOTE
// We don't care about result, this is a 'silent' save to remove empty items,
// but also to ensure the final sorted order is saved to the server
this._templateService.patchTemplate(this.template).subscribe();
this._templateService.selectedTemplate = null;
}
I understand that doing synchronous calls is not recommended as it blocks the UI/whole browser, which is not great.
I am sure there are multiple ways to solve this but really don't know which is the best (especially since Angular does not support sync requests so I would have to fall back to standard ajax to do that).
One idea I did think of was that the ngOnDestroy could pass a 'marker' to the API, and it could then mark that object as 'processing'. When the list component does its call, it could inspect each object to see if it has that marker and show a 'refresh stale data' button for any object in that state (which 99% of the time would only be a single item anyway, the most recent one the user edited). Seems a bit of a crap workaround and requires a ton of extra code compared to just changing an async call to a sync call.
Others must have encountered similar issues, but I cannot seem to find any clear examples except this sync one.
EDIT
Note that this child component already has a CanDeactive guard on it. It asks the user to confirm (ie. discard changes). So if they click to confirm, then this cleanup code in ngOnDestroy is executed. But note this is not a typical angular form where the user is really 'discarding' changes. Essentially before leaving this page the server has to do some processing on the final set of data. So ideally I don't want the user to leave until ngOnDestroy has finished - how can I force it to wait until that api call is done?
My CanDeactive guard is implemented almost the same as in the official docs for the Hero app, hooking into a general purpose dialog service that prompts the user whether they wish to stay on the page or proceed away. Here it is:
canDeactivate(): Observable<boolean> | boolean {
console.log('deactivating');
if (this.template.template_items.filter((obj) => { return !obj.is_completed}).length < 2)
return true;
// Otherwise ask the user with the dialog service and return its
// observable which resolves to true or false when the user decides
return this._dialogService.confirm('You have some empty items. Is it OK if I delete them?');
}
The docs do not make it clear for my situation though - even if I move my cleanup code from ngOnDestroy to a "YES" method handler to the dialog, it STILL has to call the api, so the YES handler would still complete before the API did and I'm back with the same problem.
UPDATE
After reading all the comments I am guessing the solution is something like this. Change the guard from:
return this._dialogService.confirm('You have some empty items.
Is it OK if I delete them?');
to
return this._dialogService.confirm('You have some empty items.
Is it OK if I delete them?').subscribe(result => {
...if yes then call my api and return true...
...if no return false...
});
As you said, there are many ways and they depend on other details how your whole app, data-flow and ux-flow is setup but it feels like you might want to take a look at CanDeactivate guard method which ensures user cannot leave route until your Observable<boolean>|Promise<boolean> are resolved to true.
So, its a way for async waiting until your service confirms things are changed on server.
[UPDATE]
it depends on your user confirmation implementation but something along these lines...
waitForServiceToConfirmWhatever(): Observable<boolean> {
return yourService.call(); //this should return Observable<boolean> with true emitted when your server work is done
}
canDeactivate(): Observable<boolean> {
if(confirm('do you want to leave?') == true)
return this.waitForServiceToConfirmWhatever();
else
Observable.of(false)
}
One "workaround" I can think of is to have your list based in client. You have the list as a JS array or object and show the UI based on that. After editing in the details screen, have a stale flag on the item which the service called on ngOnDestroy clears while updating the other related data.

Change document title after ajax call

I would like to be able to set the document title on each page of my React app. While I realise I can use document.title I'm not sure how to go about setting the title when it depends on data fetched via an async call. I'm using Redux for the server fetches.
Inside my component class I have:
componentDidMount() {
this.props.dispatch(fetchJob(this.props.match.params.slug))
}
but I'm not sure how to set the title once the call has finished?
You can use a callback to set the document title after the call is complete, as with any javascript promise. For more info, have a look at the then() method for Promises:
The then() method returns a Promise. It takes up to two arguments: callback functions for the success and failure cases of the Promise.
So your code would be:
componentDidMount() {
this.props.dispatch(fetchJob(this.props.match.params.slug)).then(() => {
document.title = "Your title";
})
}
I strongly suggest to use a middleware (you can create your own one) to manage things like: animations, logging, analytics, etc.
So you won't dirt the logic of your application.
If you want it being part of your application, implement such logic inside your component inside componentWillReceiveProps or componentDidUpdate (I suggest the first one), and update the slug. Of course, you should put a guard in this part of code, a condition of when to update your title

Ember - observer in previous route fires when property set in didTransition hook of next route

In my index route, I have an observer that fires an action when the user object is set in the session service, as shown below. This works fine- the console logs 'index route observer fired' and the action fires.
routes/index
session: Ember.inject.service(),
sendTheAction: function() {
console.log('index route observer fired');
this.send('checkLicense');
}.observes('session.user'),
actions: {
checkLicense: function() {
if (this.get('session.user.email)) {
//get the user's email and send off an AJAX request.
}
},
}
I also have a logout route which (among other things) sets session.user to an empty object, and then transitions to the login route. I simply use a link-to helper pointing to 'logout' to initiate this.
routes/logout
session: Ember.inject.service(),
actions: {
didTransition: function() {
this.set('session.user', {});
this.transitionTo('login');
}
}
If I am in the index route and I click logout, the transition to logout begins. Then, when session.user is set to {} in the logout route's didTransition hook, the observer in the index route fires. The console logs 'index route observer fired' but then I get a console error saying
Error while processing route: logout Nothing handled the action 'checkLicense'.
I'm not sure how the observer in the previous route can still be fired by a change in the logout route's didTransition hook, as I thought didTransition only fired when the transition was fully resolved.
I think that it is then looking for the action "checkLicense" in the logout route.
Can anyone clarify why that observer still fires, and how to get around this?
The observer still fires and this is the expected behavior. The reason is; routes are SINGLETONs; they are not destroyed upon transitions. So; if you define an observer within a route and watch for values that might be updated by other routes, components, controllers, etc. then your observer will still work!!! So; be careful about using observers within singletons (controllers, services, routest, etc.).
I created a twiddle that shows the exact case you mentioned. The valid question here in fact is "why is the send action within index.js is delegated to the other route?" Here is the answer from the Ember.js api. It says "Sends an action to the router, which will delegate it to the currently active route..." in send method definition. So; the error occurs; because current active route is not index anymore; action is not found!
So; what can you do? Why are throwing an action to yourself within index.js? Just make a normal function call!!! If you still need the action; you can also call the same function from within the action. See the modified twiddle in order to see what I mean.
To sum up; observers within singletons will work no matter what if the dependent key(s) get(s) updated! Hope; it is all clear now. Best Regards.

ReactJS - waiting for action to finish within componentDidMount

Code in question is below. I have an async UserActions call within componentDidMount, and immediately afterwards I am looking to user information from within the UserStore populated by this action. Clearly, I cannot rely upon UserStore.isLargeAccess() being defined. Is the best convention to place the code relying on the action within a callback, or am I missing some bigger design choice?
componentDidMount() {
this.changeListener = this._onChange.bind(this);
UserStore.addChangeListener(this.changeListener);
OrganizationStore.addChangeListener(this.changeListener);
// Fetch user info
UserActions.get(AuthStore.username);
// UserStore.isLargeAccess() is undefined here,
// because the previous action has not finished.
if (UserStore.isLargeAccess()) {
OrganizationActions.getUsers(this.state.organization);
}
if (UserStore.isGlobalAccess()) {
OrganizationActions.getOrganizations();
}
}
How it should to work (If I understand your flow):
You should register different callbacks for each of your stores (otherwise you don't know which store emit event)
You start some async work.
When async work is finished then it dispatch action with some data from async work
your stores UserStore and OrganizationStore listens for this action, and when they receive it, they do some job and emit change.
When they emit change, then they call callbacks from your component. In each callback you know which store invoke it, And therefore you know from what store get data.

Categories

Resources