Read route params from directly entered url in app - javascript

My question would be regarding angular 4, how to get route params, if for example a user gets on your page with, instead of the default url, like for example http://localhost:3000/, to something like http://localhost:3000/user/:id, and to be able to pick up the :id from that url (user has directly entered it in the browser, not navigating through the app).
In the example bellow same component is used, mainly because of needing to catch that id and dispatch other actions, if its present, and that would be it.
I have tried playing around with ActivatedRoute but from what I could tell so far, that only works when navigation throughout the app, from within the app, not in this case, which always returns a null value if that url is directly entered in the browser, it gets redirected to the default / route and that would be it.
Any tips or pointers are much appreciated
app.routing-module.ts
import {hookComponent} from './hook.component';
import {RouterModule, Routes} from '#angular/router';
import {NgModule} from '#angular/core';
export const routes: Routes = [
{
path: '',
component: HookComponent
},
{
path: 'user/:id',
component: HookComponent
}
];
#NgModule({
imports: [RouterModule.forRoot(routes, { enableTracing: true })],
exports: [RouterModule]
})
export class AppRoutingModule {}
hook.component
import {Component, EventEmitter, Input, OnInit, ViewChild} from '#angular/core';
import { ActivatedRoute, ParamMap} from '#angular/router';
#Component({
selector: 'hook',
templateUrl: 'hook.component.html',
styleUrls: ['hook.component.scss']
})
export class HookComponent implements OnDestroy, OnInit {
constructor(private route: ActivatedRoute) {
}
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
console.log('params are', params); //null?
});
}
}

Your way is already ok, but in your example params is an array and you can access to :id by calling params['id']:
this.sub = this.route.params.subscribe(params => {
console.log('params are', params['id']);
});
Here is an working example on stackblitz.

Access current url via Location
public constructor(location:Location) {
let url = location.prepareExternalUrl(location.path());
}
and parse out id from this.

If all you want to do is log the params.id; try using the ActivatedRouteSnapshot like this.
ngOnInit() {
console.log(this.route.snapshot.params.id);
}
If you want to check if the params.id is present, maybe do something like:
import {Component, EventEmitter, Input, OnInit, ViewChild} from '#angular/core';
import { ActivatedRoute, ParamMap} from '#angular/router';
#Component({
selector: 'hook',
templateUrl: 'hook.component.html',
styleUrls: ['hook.component.scss']
})
export class HookComponent implements OnDestroy, OnInit {
hasId: boolean = false;
constructor(private route: ActivatedRoute) {
}
ngOnInit() {
if(this.route.snapshot.params.id !== null)
{
// do magic....
}
}
}

Related

Getting language prefix as parameter from Angular 6 router

I have an Angular (6.0.3) application and I want to use the language as a common prefix to all routes, something like /en/foo/bar. My route definition src/app/submodule/submodule.routes.ts looks like this:
export const submoduleRoutes = [
{ path: ':lingua/foo/bar', component: FooBarComponent },
];
My root component src/app/app.component.ts:
import { Component, OnInit, OnDestroy } from '#angular/core';
import { Router, ActivatedRoute, Event, NavigationEnd } from '#angular/router';
import { filter } from 'rxjs/operators';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
title = 'app';
private sub: any;
constructor(private router: Router, private route: ActivatedRoute) {
this.router.events.pipe(
filter((event:Event) => event instanceof NavigationEnd)
).subscribe(x => console.log(x));
};
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
console.log(params);
});
}
ngOnDestroy() {
this.sub.unsubscribe();
}
}
When I navigate to /xy/foo/bar, the FooBarComponent gets rendered. But I am not able to retrieve language prefix in the root component. The params hash is empty.
The console output first displays {} for the empty params, and then the NavigationEnd event.
The same code works as expected in the FooBarComponent. Obviously, the root component is the wrong place to get the parameters.
My goal is actually to get the lingua parameter for all components, so that I can set the correct language. I know that I could also store the language in a cookie or local storage but I need bookmarkable URLs for the multi-lingual content.
In case that matters, the source code is at https://github.com/gflohr/Lingua-Poly (commit 12608dc). The route in question is defined in https://github.com/gflohr/Lingua-Poly/blob/master/src/app/main/main.routes.ts (at the end, :lingua/mytest.
You need to make them children of the App.component for it to work.
For example:
const routes = [
{ path: ":lingua", component: AppComponent, loadChildren: 'app/main/main.module#MainModule' },
{ path: "", pathMatch: "full", redirectTo: "/en" }
]
This will load all the child routes from main module, but allow AppComponent to access the lingua parameter.
You will then need to remove the :lingua part from your child route
Here is a StackBlitz example

Supplied parameters do not match any signature of call target error is thrown on Instantiating of Class

I am trying to wrap each of router.navigateByUrl in a function of a class and plan to call that function in relevant place. But doing so throwing 'Supplied parameters do not match any signature of call target'. I have followed few other links in SO but none seems to be helpful in my case
commonRouter.ts
// have wrapped navigation to home in homePage
// so wherever is needed this homePage will be called instead of
//this.router.navigateByUrl('/home');
import {Router} from '#angular/router';
export class RouterComponent{
router:any;
constructor(private rt:Router){
this.router=rt;
}
homePage(){
this.router.navigateByUrl('/home');
}
}
someComponent.ts
// Importing the newly created typescript file
import {RouterComponent} from './../../app-routing-component';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.less']
})
export class LoginComponent implements OnInit {
private ms:MainService= new MainService();
//Instantiating RouterComponent
private rt:RouterComponent = new RouterComponent(); // this line throwing error
constructor(private fb:FormBuilder) {}
someMethod(){
rt.homePage() // Calling homePage
}
//... rest of code
}
app-routing.module.ts
// module where all the paths and component are declared
import {NgModule} from "#angular/core";
import {RouterModule, Routes} from "#angular/router";
import {HomeComponent} from "./home/home/home.component";
const routes: Routes = [
{
path: 'login', component: LoginComponent,
}, {
path: 'home', component: HomeComponent,
children: [{
path: "account",
component: AccountsComponent
},{
path: '**',
component: PageNotFoundComponent
}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {
}
Your RouterComponent requires a Router argument. Router is an injectable, thus would be resolvable if Angular knows how to handle your RouterComponent class.
It would be best to decorate your class as Injectable and inject the value at the Angular component. e.g.
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
#Injectable()
export class RouterService {
constructor(private router: Router) { }
homePage(){
this.router.navigateByUrl('/home');
}
};
Register it in your module or add as dependency to the providers field in the Component decorator and import it into your components.
import { Component } from '#angular/core';
import { RouterService } from '...';
#Component({ ... })
export class LoginComponent {
constructor(private router: RouterService) { }
toHomePage() {
this.router.homePage();
}
};
Because it is an Injectable, Angular knows how to resolve the parameters.
The choice of namingconvention for your RouterComponent class would led others to think it is decorated as an Angular component, but you are using it as a service.

How can I hide my Navigation Bar if the component is called on all my routes (Angular4)

I know variations of this question have been asked millions of times before but I just cant seem to solve my problem.
So i'm making an accounting site and my problem is that I cant seem to be able to hide the top navigation bar from the login page and still keep it on all my other pages/routes:
I call the Navigation Bar component in app.component.html so it shows on all my pages:
(app.component.html)
<app-navbar>
<router-outlet>
The login page has simple authentication as i'm still making the template, eventually, the username and password will come from a back-end database.
The login page ts file looks like this:
export class LoginComponent implements OnInit {
emailFormControl = new FormControl('', [Validators.required,
Validators.pattern(EMAIL_REGEX)]);
UserName = new FormControl('', [Validators.required]);
password = new FormControl('', [Validators.required]);
constructor(private router: Router) { }
ngOnInit() {
}
loginUser(e) {
e.preventDefault();
console.log(e);
const username = e.target.elements[0].value;
const password = e.target.elements[1].value;
if (username === 'admin' && password === 'admin') {
// this.user.setUserLoggedIn();
this.router.navigate(['accounts']);
}
}
}
I also have a user service:
import { Injectable } from '#angular/core';
#Injectable()
export class UserService {
private isUserLoggedIn;
public username;
constructor() {
this.isUserLoggedIn = false;
}
setUserLoggedIn() {
this.isUserLoggedIn = true;
this.username = 'admin';
}
getUserLoggedIn() {
return this.isUserLoggedIn;
}
}
I've seen answers regarding Auth and such but i can't seem to sculpt the answers around my code.
How do i hide the Navigation bar on the login page?
I'd appreciate any and all help. Thank you
EDIT
This is the routing file as requested by Dhyey:
import {RouterModule, Routes} from '#angular/router';
import {NgModule} from '#angular/core';
import { LoginComponent } from './login/login.component';
import { AdminComponent } from './Components/admin/admin.component';
import { AccountsComponent } from './Components/accounts/accounts.component';
import { MappingComponent } from './Components/mapping/mapping.component';
const appRoutes: Routes = [
{ path: '',
pathMatch: 'full',
redirectTo: 'login' },
{
path: 'login',
component: LoginComponent
},
{
path: 'admin',
component: AdminComponent
},
{
path: 'accounts',
component: AccountsComponent
},
{
path: 'mapping',
component: MappingComponent
},
];
#NgModule({
imports: [
RouterModule.forRoot(appRoutes)
],
exports: [
RouterModule
]
})
export class AppRoutingModule {}
// export const routingComponents = [MappingComponent, AccountsComponent, AdminComponent, LoginComponent];
EDIT 2
This is the app.component.ts file
import { Component } from '#angular/core';
import {FormControl} from '#angular/forms';
import {HttpModule} from '#angular/http';
import { UserService } from './services/user.service';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.sass']
})
export class AppComponent {
title = 'app';
myControl: FormControl = new FormControl();
}
You can check if user is logged in through your getUserLoggedIn method:
First you need to inject UserService in app.component.ts:
constructor(public userService: UserService ) { }
Then in your html:
<app-navbar *ngIf="userService.getUserLoggedIn()">
<router-outlet>
This way only when isUserLoggedIn is true, the app-navbar will be shown
What you can also do is let your menu in app component (this is not the problem), and use a route condition to display it or not.
For that, angular provides ActivatedRoute to get info from the current route url https://angular.io/api/router/ActivatedRoute
Import ActivatedRoute or Route should be fine too
Inject in component
Using constructor :
constructor (private activatedRoute: ActivatedRoute / Route) {
this.currentPage = activatedRoute.url;
}
Then check onInit the route info like below
if (this.curentPage === 'admin') { this.displayMenu = false; }
Finally, use your <div class="menu" *ngIf="! displayMenu">...</div>

Angular Server-Side Rendering with Route Resolve

I am attempting to use Server-Side Rendering in Angular (v4) to allow for better SEO.
Things work as expected until I add resolve on my route. Adding resolve causes HTML title to retain it's initial value when viewing source.
My Module:
import {
Injectable,
ModuleWithProviders,
NgModule
} from '#angular/core';
import {
ActivatedRouteSnapshot,
Resolve,
Router,
RouterModule,
RouterStateSnapshot
} from '#angular/router';
import {
Observable
} from 'rxjs/Rx';
import {
ArticleComponent
} from './article.component';
import {
Article,
ArticlesService,
UserService,
SharedModule
} from '../shared';
#Injectable()
export class ArticleResolver implements Resolve < Article > {
constructor(
private articlesService: ArticlesService,
private router: Router,
private userService: UserService
) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): any {
return this.articlesService.get(route.params['slug'])
.catch((err) => this.router.navigateByUrl('/'));
}
}
const articleRouting: ModuleWithProviders = RouterModule.forChild([{
path: 'article/:slug',
component: ArticleComponent,
resolve: {
article: ArticleResolver
},
data: {
preload: true
}
}]);
#NgModule({
imports: [
articleRouting,
SharedModule
],
declarations: [
ArticleComponent
],
providers: [
ArticleResolver
]
}) export class ArticleModule {}
My Component:
import {
Component,
OnInit
} from '#angular/core';
import {
ActivatedRoute,
Router,
} from '#angular/router';
import {
Title,
Meta
} from '#angular/platform-browser';
import {
AppComponent
} from '../app.component';
import {
Article,
} from '../shared';
#Component({
selector: 'article-page',
templateUrl: './article.component.html'
})
export class ArticleComponent implements OnInit {
article: Article;
constructor(
private route: ActivatedRoute,
private meta: Meta,
private title: Title
) {}
ngOnInit() {
this.route.data.subscribe(
(data: {
article: Article
}) => {
this.article = data.article;
}
);
this.title.setTitle(this.article.title);
}
}
I am new to Angular SSR so any guidance is greatly appreciated.
Instead of subscribing to route data, retrieve your results from the snapshot like this:
this.route.snapshot.data['article']
You also might need to register ArticlesService in your providers for the module.
As a side note, this import:
import {
Observable
} from 'rxjs/Rx';
is an RxJS antipattern. Please use the following import instead:
import {Observable} from 'rxjs/Observable';
I found that my primary service was referencing a secondary service that was attempting to return an authentication token from window.localStorage.
Attempting to access the client storage caused Angular SSR to omit the generation of source code for my component.
Thanks #Adam_P for helping me walk through it!

How do i send data from component A to component B Angular 2

I want to display the username on my NavbarComponent, the data coming from LoginComponent.
login.component.ts
import { Component, OnInit } from '#angular/core';
import { FormBuilder,FormGroup,Validators,FormControl } from '#angular/forms';
import { AuthService } from '../../services/auth.service';
import { Router } from '#angular/router';
import { FlashMessagesService } from 'angular2-flash-messages';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
form : FormGroup;
message;
messageClass;
constructor(
private formBuilder: FormBuilder,
private authService:AuthService,
private router: Router,
private flashMessagesService:FlashMessagesService
) {
this.createForm();
}
createForm(){
this.form=this.formBuilder.group({
username:['', Validators.required],
password:['', Validators.required]
})
}
onLoginSubmit(){
const user={
username: this.form.get('username').value,
password: this.form.get('password').value
}
this.authService.login(user).subscribe(data=>{
if(!data.success){
this.messageClass="alert alert-danger";
this.message=data.message;
}
else{
this.messageClass="alert alert-success";
this.message=data.message;
this.authService.storeUserData(data.token,data.user);
setTimeout(()=>{
this.router.navigate(['/profile']);
},2000);
this.flashMessagesService.show('Welcome to bloggy, '+ this.form.get('username').value +' !',{cssClass: 'alert-info'});
}
});
}
}
navbar.component.ts
import { Component, OnInit } from '#angular/core';
import { AuthService } from '../../services/auth.service';
import { Router } from '#angular/router';
import { FlashMessagesService } from 'angular2-flash-messages';
#Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
usernameNav;
constructor(
private authService:AuthService,
private router:Router,
private flashMessagesService:FlashMessagesService
) { }
onLogoutClick(){
this.authService.logout();
this.flashMessagesService.show('You are logged out',{cssClass: 'alert-info'});
this.router.navigate(['/']);
}
ngOnInit() {
}
}
I am sorry if there's too much code but basically i want to take data.user.username from LoginComponent in the onLoginSubmit() function, send it to NavbarComponent, use it in a variable and display it on the html.
I tried to import the NavbarComponent, didn't work.
Pretty Interesting question , basically solution to your problem is Observable/subscriber, you need to
listen when the value changes in the login component and send it back to navbar component to display.
you can use global Observable like this
let suppose you create one Observable in your global file like this
public loggedInObs: Rx.Subject<any> = new Rx.Subject<any>();
public loggedInVar: boolean = false;
for using this you have to import some dependency like this
import { Observable } from 'rxjs/Rx';
import * as Rx from 'rxjs/Rx';
import 'rxjs/add/observable/of';
import 'rxjs/Rx';
import 'rxjs/add/operator/map';
Than in your login component you tell angular that there are some changes occurred like user login successfully.
and fire observable , so that angular will able to listen in whole app where you set subscriber to listen that user
have logged in into app. code for this as below
this.authService.login(user).subscribe(data=>{
if(!data.success){
this.messageClass="alert alert-danger";
this.message=data.message;
}
else{
this.messageClass="alert alert-success";
this.message=data.message;
localStorage.setItem('user_info', JSON.stringify(data.user))
/*for Global level observable fire here*/
this.global_service.loggedInVar = true; //i assume global_service here as your Global service where we declared variables
this.global_service.loggedInObs.next(this.global_service.loggedInVar);
this.authService.storeUserData(data.token,data.user);
setTimeout(()=>{
this.router.navigate(['/profile']);
},2000);
this.flashMessagesService.show('Welcome to bloggy, '+ this.form.get('username').value +' !',{cssClass: 'alert-info'});
}
now you can listen to this using subscriber everywhere in the app like this in your navbar component
userdata = JSON.parse(localStorage.getItem('user_info')); // in case of normal loading page
this.userName = userdata.user_name
this.global_service.loggedInObs.subscribe(res => { // in case of when without refresh of page
console.log('changes here in login');
userdata = JSON.parse(localStorage.getItem('user_info'));
this.userName = userdata.user_name
})
if any doubt let me know.

Categories

Resources