I'm trying to implement a solution for the login page. When user passes the wrong login/password I want the page to reload after 3 seconds. I was trying to do it with 2 solutions but can't make it work. The code would launch after click on a button when there is no match for credentials (else condition):
FIRST IDEA:
...else{
this.comUser = 'WRONG LOGIN OR PASSWORD'
this.alert = ""
setTimeout(function() {
window.location = "I would like it to use the routing to a component instead of URL here"; }
,3000);
SECOND IDEA WITH A PROMISE:
const reloadDelay = () => {
reload = this.router.navigate(['/login']);
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("działa");
resolve(reload)
}, 4000);
});
};
Thanks for any hints!
If you're using angular 9 you can try this
setTimeout(() => {this.domDocument.location.reload()}, 3000);
You need to import:
import { Inject } from '#angular/core';
import { DOCUMENT } from '#angular/common';
In order to use the above-mentioned method and have the constructor of component.ts configured as below.
constructor(#Inject(DOCUMENT) private domDocument: Document){}
This can now be done using the onSameUrlNavigation property of the Router config. In your router config enable onSameUrlNavigation option, setting it to reload . This causes the Router to fire an events cycle when you try to navigate to a route that is active already.
#ngModule({
imports: [RouterModule.forRoot(routes, {onSameUrlNavigation: 'reload'})],
exports: [RouterModule],
})
In your route definitions, set runGuardsAndResolvers to always. This will tell the router to always kick off the guards and resolvers cycles, firing associated events.
export const routes: Routes = [
{
path: 'invites',
component: InviteComponent,
children: [
{
path: '',
loadChildren: './pages/invites/invites.module#InvitesModule',
},
],
canActivate: [AuthenticationGuard],
runGuardsAndResolvers: 'always',
}
]
Finally, in each component that you would like to enable reloading, you need to handle the events. This can be done by importing the router, binding onto the events, and invoking an initialization method that resets the state of your component and re-fetches data if required.
export class InviteComponent implements OnInit, OnDestroy {
navigationSubscription;
constructor(
// … your declarations here
private router: Router,
) {
// subscribe to the router events. Store the subscription so we can
// unsubscribe later.
this.navigationSubscription = this.router.events.subscribe((e: any) => {
// If it is a NavigationEnd event re-initalise the component
if (e instanceof NavigationEnd) {
this.initialiseInvites();
}
});
}
initialiseInvites() {
// Set default values and re-fetch any data you need.
}
ngOnDestroy() {
if (this.navigationSubscription) {
this.navigationSubscription.unsubscribe();
}
}
}
With all of these steps in place, you should have route reloading enabled. Now for your timeperiod, you can simply use settimeout on route.navigate and this should reload it after desired time.
Related
I was using the event emitter from vue to make the main app talk to the components and it was working as long as I had the app and all the components in one .js file.
But now I wish to have them separated, one component per file, and obviously I cannot use the event emitter anymore, because the app appears undefined in the modules.
What do I need to change to be able have the app and components communicate again?
My code:
my-app.js
import My from '/my-module.js';
const app = new Vue({
router: new VueRouter({
mode: 'history',
routes: [{
path: '/',
component: My,
}]),
methods: {
created(){
this.ws = new WebSocket(...);
this.ws.onmessage = event => {
this.$emit('message', e);
}
}
}
}).$mount('#app');
my-module.js
export default {
template: `....`
created(){
// I want to be able to access 'app' here
// or another way to receive the messages
console.log(app);
app.$on('message', ...)
}
}
As you can see I only need this because I'm using websockets and the app holds the websocket client. The components need to do something when certain messages are received.
In your case, you may use those events in multiple components, specialy when app is still growing. i think you better use eventbus to emit and catch all of them.
here how to use eventbus in vuejs : https://alligator.io/vuejs/global-event-bus/.
in you're case :
import { eventBus } from 'path/to/file';
...
methods: {
created(){
this.ws = new WebSocket(...);
this.ws.onmessage = event => {
eventBus.$emit('message', e);
}
}
}
Other component :
import { eventBus } from 'path/to/file';
...
created() {
eventBus.$on('message',() => ) {
// code
}
}
How can i get params in root component? (app.component.ts)
I have such app.component.ts (i'm using Angular/Cli):
...
import {Transition} from "#uirouter/angular";
...
export class AppComponent {
id: any;
constructor(private trans: Transition) {
this.id = trans.params().someId;
}
}
but i get:
ERROR Error: No provider for Transition!
But if i use the same logic in any inner component (which has route) - everything is fine. What i do wrong?
Also!
I'm using ngx-restangular. And i have in app.module.ts:
// Function for settting the default restangular configuration
export function RestangularConfigFactory (RestangularProvider, authService) {
RestangularProvider.setBaseUrl('http://api.test.com/v1');
// This function must return observable
var refreshAccesstoken = function () {
// Here you can make action before repeated request
return authService.functionForTokenUpdate();
};
RestangularProvider.addErrorInterceptor((response, subject, responseHandler) => {
if (response.status === 403) {
/*Here somehow I need to get route params too, is it possible, and how?*/
refreshAccesstoken()
.switchMap(refreshAccesstokenResponse => {
//If you want to change request or make with it some actions and give the request to the repeatRequest func.
//Or you can live it empty and request will be the same.
// update Authorization header
response.request.headers.set('Authorization', 'Bearer ' + refreshAccesstokenResponse)
return response.repeatRequest(response.request);
})
.subscribe(
res => responseHandler(res),
err => subject.error(err)
);
return false; // error handled
}
return true; // error not handled
});
}
// AppModule is the main entry point into Angular2 bootstraping process
#NgModule({
bootstrap: [ AppComponent ],
imports: [
// Importing RestangularModule and making default configs for restanglar
RestangularModule.forRoot([authService], RestangularConfigFactory),
],
})
how i can get there route params as well?
Option 1: route to root component
If you currently have in index.html:
<root-component></root-component> <!-- has a ui-view inside it -->
and
bootstrap: RootComponent
You can switch to bootstrapping a UIView:
<ui-view></ui-view>
and
bootstrap: UIView
then route to your root component
const rootState = { component: RootComponent }
then your root component will be able to inject the initial Transition
See the sample app for an example:
https://github.com/ui-router/sample-app-angular/blob/e8f8b6aabd6a51bf283103312930ebeff52fe4c3/src/app/app.module.ts#L37
https://github.com/ui-router/sample-app-angular/blob/e8f8b6aabd6a51bf283103312930ebeff52fe4c3/src/app/app.states.ts#L7-L18
Option 2: Use the transition start observable
class RootComponent {
constructor(router: UIRouter) {
this.subscription = router.globals.start$.subscribe((trans: Transition) => console.log(trans)))
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Option 3: Use a transition hook
If you're trying to do some action for the initial transition, do this in a transition hook
function configFn(router: UIRouter) {
router.transitionService.onStart({}, function(trans: Transition) {
if (trans.$id === 0) {
return somepromise; // Allows async, etc
}
}
}
I have built an app that uses router 3.0.0-beta.1 to switch between app sections. I also use location.go() to emulate the switch between subsections of the same page. I used <base href="/"> and a few URL rewrite rules in order to redirect all routes to index.html in case of page refresh. This allows the router to receive the requested subsection as a URL param. Basically I have managed to avoid using the HashLocationStrategy.
routes.ts
export const routes: RouterConfig = [
{
path: '',
redirectTo: '/catalog',
pathMatch: 'full'
},
{
path: 'catalog',
component: CatalogComponent
},
{
path: 'catalog/:topCategory',
component: CatalogComponent
},
{
path: 'summary',
component: SummaryComponent
}
];
If I click on a subsection in the navigation bar 2 things happen:
logation.go() updates the URL with the necessary string in order to indicate the current subsection
A custom scrollTo() animation scrolls the page at the top of the requested subsection.
If I refresh the page I am using the previously defined route and extract the necessary parameter to restore scroll to the requested subsection.
this._activatedRoute.params
.map(params => params['topCategory'])
.subscribe(topCategory => {
if (typeof topCategory !== 'undefined' &&
topCategory !== null
) {
self.UiState.startArrowWasDismised = true;
self.UiState.selectedTopCategory = topCategory;
}
});
All works fine except when I click the back button. If previous page was a different section, the app router behaves as expected. However if the previous page/url was a subsection, the url changes to the previous one, but nothing happens in the UI. How can I detect if the back button was pressed in order to invoke the scrollTo() function to do it's job again?
Most answers I saw relly on the event onhashchange, but this event does not get fired in my app since I have no hash in the URL afterall...
I don't know if the other answers are dated, but neither of them worked well for me in Angular 7. What I did was add an Angular event listener by importing it into my component:
import { HostListener } from '#angular/core';
and then listening for popstate on the window object (as Adrian recommended):
#HostListener('window:popstate', ['$event'])
onPopState(event) {
console.log('Back button pressed');
}
This worked for me.
Another alternative for this issue would be to subscribe to the events emitted by the Angular Router service. Since we are dealing with routing, it seems to me that using Router events makes more sense.
constructor(router: Router) {
router.events
.subscribe((event: NavigationStart) => {
if (event.navigationTrigger === 'popstate') {
// Perform actions
}
});
}
I would like to note that popstate happens when pressing back and forward on the browser. So in order to do this efficiently, you would have to find a way to determine which one is occurring. For me, that was just using the event object of type NavigationStart which gives information about where the user is coming from and where they are going to.
To detect browser back button click
import platformlocation from '#angular/common and place the below code in your constructor :
constructor(location: PlatformLocation) {
location.onPopState(() => {
alert(window.location);
}); }
This is the latest update for Angular 13
You have to first import NavigationStart from the angular router
import { NavigationStart, Router } from '#angular/router';
Then add the following code to the constructor
constructor(private router: Router) {
router.events.forEach((event) => {
if(event instanceof NavigationStart) {
if (event.navigationTrigger === 'popstate') {
/* Do something here */
}
}
});
}
Angular documentation states directly in PlatformLocation class...
This class should not be used directly by an application developer.
I used LocationStrategy in the constructor
constructor(location: LocationStrategy) {
location.onPopState(() => {
alert(window.location);
});
}
A great clean way is to import 'fromEvent' from rxjs and use it this way.
fromEvent(window, 'popstate')
.subscribe((e) => {
console.log(e, 'back button');
});
Using onpopstate event did the trick:
window.addEventListener('popstate',
// Add your callback here
() => self.events.scrollToTopCategory.emit({ categId: self.state.selectedTopCategory })
);
I agree with Adrian Moisa answer,
but you can use "more Angular 2 way" using class PlatformLocation by injecting to your component or service, then you can define onPopState callback this way:
this.location.onPopState(()=>{
// your code here...
this.logger.debug('onpopstate event');
});
Simpler way - Link
import { PlatformLocation } from '#angular/common';
import { NgbModal } from '#ng-bootstrap/ng-bootstrap';
...
constructor(
private platformLocation: PlatformLocation ,
private modalService: NgbModal
)
{
platformLocation.onPopState(() => this.modalService.dismissAll());
}
I honestly don't know your use case. And the thread is quite old. But this was the first hit on Google. And if someone else is looking for this with Angular 2 and ui-router (just as you are using).
It's not technically detecting the back button. It's more detecting whether you as a developer triggered the state change or whether the user updated the URL themselves.
You can add custom options to state changes, this can be done via uiSref and via stateService.go. In your transitions, you can check whether this option is set. (It won't be set on back button clicks and such).
Using ui-sref
<a uiSref="destination-name" [uiOptions]="{custom: {viaSref: true}}">Bar</a>
Using state.go
import {StateService} from '#uirouter/core';
...
#Component(...)
export class MyComponent {
constructor(
private stateService: StateService
) {}
public myAction(): void {
this.stateService.go('destination-name', {}, {custom: {viaGo: true}});
}
}
You can detect it in any transition hook, for example onSucces!
import {Transition, TransitionOptions, TransitionService} from '#uirouter/core';
...
#Component(...)
export class MyComponent implements OnInit {
constructor(
private transitionService: TransitionService
) {}
public ngOnInit(): void {
this.transitionService.onSuccess({}, (transition: Transition) => this.handleTransition(Transition));
}
private handleTransition(transition: Transition): void {
let transitionOptions: TransitionOptions = transition.options();
if (transitionOptions.custom?.viaSref) {
console.log('viaSref!');
return;
}
if (transitionOptions.custom?.viaGo) {
console.log('viaGo!');
return;
}
console.log('User transition?');
}
}
You can check the size of "window.history", if the size is 1 you can't go back.
isCanGoBack(){
window.history.length > 1;
}
I'm trying to navigate to a route in Angular 2 with a mix of route and query parameters.
Here is an example route where the route is the last part of the path:
{ path: ':foo/:bar/:baz/page', component: AComponent }
Attempting to link using the array like so:
this.router.navigate(['foo-content', 'bar-contents', 'baz-content', 'page'], this.params.queryParams)
I'm not getting any errors and from what I can understand this should work.
The Angular 2 docs (at the moment) have the following as an example:
{ path: 'hero/:id', component: HeroDetailComponent }
['/hero', hero.id] // { 15 }
Can anyone see where I'm going wrong? I'm on router 3.
If the first segment doesn't start with / it is a relative route. router.navigate needs a relativeTo parameter for relative navigation
Either you make the route absolute:
this.router.navigate(['/foo-content', 'bar-contents', 'baz-content', 'page'], this.params.queryParams)
or you pass relativeTo
this.router.navigate(['../foo-content', 'bar-contents', 'baz-content', 'page'], {queryParams: this.params.queryParams, relativeTo: this.currentActivatedRoute})
See also
https://github.com/angular/angular.io/blob/c61d8195f3b63c3e03bf2a3c12ef2596796c741d/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts#L108
https://github.com/angular/angular/issues/9476
import { ActivatedRoute } from '#angular/router';
export class ClassName {
private router = ActivatedRoute;
constructor(r: ActivatedRoute) {
this.router =r;
}
onSuccess() {
this.router.navigate(['/user_invitation'],
{queryParams: {email: loginEmail, code: userCode}});
}
}
Get this values:
---------------
ngOnInit() {
this.route
.queryParams
.subscribe(params => {
let code = params['code'];
let userEmail = params['email'];
});
}
Ref: https://angular.io/docs/ts/latest/api/router/index/NavigationExtras-interface.html
As simpler as
import { Router } from '#angular/router';
constructor( private router:Router) {}
return(){this.router.navigate(['/','input']);}
Here you will be redirecting to route input .
If you wish to go to particular path with relative to some path then.
return(){this.router.navigate(['/relative','input']);}
Here on return() is the method we will be triggered on a button click
<button (click)=return()>Home
Lets say we have a component in Aurelia named UserRouter, which is a child router and handles routing to UserProfile, UserImages and UserFriends.
I want the UserRouter to load in the user from the API (on canActivate) and then pass this this user data to sub components.
Loading in the data is fine, how do I pass it down to sub components so they can all read it? e.g. placing an attribute on <router-view>.
I've tried the bindingContext argument on the bind() method of sub components but this hasn't worked.
Thanks
The way I did this, was by adding additional information to the child router definition eg:
configureRouter(config, router){
config.map([
{ route: [''], name: 'empty', moduleId: './empty', nav: false, msg:"choose application from the left" },
{ route: 'ApplicationDetail/:id', name: 'applicationDetail', moduleId: './applicationDetail', nav: false, getPickLists : () => { return this.getPickLists()}, notifyHandler : ()=>{return this.updateHandler()} }
]);
in the first route from this example, I pass in a text message by adding my own porperty 'msg' to the route object.
in the second route I pass in some event handlers, but could have been some custom objects or antything else.
In the childmodel I receive these additional elements in the activate() method:
export class empty{
message;
activate(params, routeconfig){
this.message=routeconfig.msg || "";
}
}
I guess in your case, you would add the user object to the child router definition.
You could bind the router/user object to the <router-view>
<router-view router.bind="router"></router-view>
I have the same issue, so I'd love to see what you come up with!!
You could use the event aggregator to publish out an event with the data and the sub components could subscribe and listen for that event. This would avoid coupling between them. However, there may be a downside I'm not thinking of.
I worked out a solution for this, you simply tell the dependency injector to inject an instance of your parent router component using Parent.of(YourComponent).
import {inject, Parent} from 'aurelia-framework';
import {UsersRouter} from './router';
#inject(Parent.of(UsersRouter))
export class UserImages {
constructor(usersRouter) {
this.usersRouter = usersRouter;
}
activate() {
this.user = this.usersRouter.user;
}
}
You can also actually miss out the Parent.of part because the Aurelia DI system works its way up.
import {inject} from 'aurelia-framework';
import {UsersRouter} from './router';
#inject(UsersRouter)
export class UserImages {
constructor(usersRouter) {
this.usersRouter = usersRouter;
}
activate() {
this.user = this.usersRouter.user;
}
}
You can implement a generic method to pass objects:
configureRouter(config, router){
this.router = router;
...
this.router.navigateWithParams = (routeName, params) => {
let routerParams = this.router.routes.find(x => x.name === routeName);
routerParams.data = params;
this.router.navigate(routeName);
}
}
then you can use it programmatically like:
goToSomePage(){
let obj = { someValue: "some data", anotherValue: 150.44}
this.router.navigateWithParams("someRoute", obj );
}
finally, you can get the param on the destination page:
activate(urlParams, routerParams)
{
this.paramsFromRouter = routerParams.data;
}