Angular 2 Router Link with multiple parameters - javascript

I have a route like this
path: 'projects/:id/settings'
And in my header.component.html I want to have a link to this page
<a routerLink="['projects', progectId,'settings']" routerLinkActive="active">cool link</a>
I have a project.component, where when I click on some project I go on the project page. And then I need have a possibility go to projects/:id/settings or another similar route.
How can I pass progectId variable from projects.component?
Or maybe somebody knows another way to implement this.

I had the same issue for this kind of route you should use routerLink like this:
<a routerLink="/projects/{{progectId}}/settings" routerLinkActive="active">cool link</a>
and change your route path in routing module like this:
{ path: ':id/settings', component: YourComponent}
for additional information to get projectId you should follow these step:
Inject ActivatedRoute object in your component constructor.
Define a variable called projectId in your component.
Get params by activatedRoute object in ngOnInit() method.
Finally you should get projectId in your html like this: {{projectId}}
import {Router, ActivatedRoute, Params} from '#angular/router';
#Component({
selector: 'app-your-component',
templateUrl: './your-component.component.html',
styleUrls: ['./your-component.component.css']
})
export class YourComponent implements OnInit {
private projectId: string;
constructor(private activatedRoute: ActivatedRoute) {}
ngOnInit() {
this.activatedRoute.params.subscribe((params: Params) => {
this.projectId = params['id'];
});
}}

I generally do this in the component class like this
somefunctionName() {
let link = ['/summary', this.param1, this.param2];
this.router.navigate(link);
}
And then call the function from the html code like this
<a (click)="somefunctionName()">
In your case since I am assuming you are looping through your projects you could call your function with a parameter like this
<a (click)="somefunctionName(pass the parameter here)">
Then change the function definition to reflect this.

Related

How to get previous route on 404 Error page in angular?

I have some pages and when I change router link to some wrong url and it redirect me to 404 page. In this 404 Error page I have button that will redirect me to previous page. Now I have some problems that this redirect button when I click it redirects to that wrong url and again to 404.
I try to show by photo:
1)My route. write wrong url -> redirect to 404 page.
Now the problem is when I click button to redirect at the previous route "Home" it redirect me to this wrong route.
goBack() {
this._location.back();
}
You can use JS history API to achieve this.
This is a sample to match your use case
goBack() {
window.history.go(-2);
}
And this also keeps navigiation within the SPA scope (Page wouldn't refresh).
Side effect for this is that you need to make sure that the user follows a particular flow; in which he needs to be within a working page, then goes somewhere not found then get redirected to the 404 Page..
I would recommend a button that navigates to Home or Dashboard features instead.
You can achieve this by number of ways , however I think the most elegant way is to use power of Injectable services in Angular.
Here is my solution of this problem:
Create an Injectable service which will set a variable as soon as the current navigation end. The value of this variable can be used in any of the other components where ever we would need. Obviously one has to inject the service in the required component.
In my example, I have created RouterService:
import { Injectable } from '#angular/core';
import { Router, NavigationEnd } from '#angular/router';
#Injectable({ providedIn: 'root' })
export class RouterService {
private previousUrl: string = undefined;
private currentUrl: string = undefined;
constructor(public router : Router) {
this.currentUrl = this.router.url;
router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
this.previousUrl = this.currentUrl;
this.currentUrl = event.url;
};
});
}
public getPreviousUrl(){
return this.previousUrl;
}
}
To illustrate the navigation , I have created two components viz. home and not-found Components.
Below are the code snippets for both of these components :
a) home.component.ts :
import { Component, VERSION } from '#angular/core';
import { NavigationEnd, Router } from '#angular/router';
import { filter } from 'rxjs/operators';
import { RouterService } from '../../service/router.service';
#Component({
selector: 'not-home-app',
templateUrl: './home.component.html',
styleUrls: [ './home.component.scss' ]
})
export class HomeComponent {
name = 'Angular ' + VERSION.major;
constructor(private router: Router,private routerService: RouterService) {}
takeMeToNotFoundPage(){
this.router.navigate(['/notFound']);
}
}
b) not-found.component.ts:
import { Component, VERSION } from '#angular/core';
import { NavigationEnd, Router } from '#angular/router';
import { filter } from 'rxjs/operators';
import { RouterService } from '../../service/router.service';
#Component({
selector: 'not-found-app',
templateUrl: './not-found.component.html',
styleUrls: [ './not-found.component.scss' ]
})
export class NotFoundComponent {
previousUrl: string;
constructor(private router: Router, private routerService: RouterService) {
router.events
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe((event: NavigationEnd) => {
console.log('prev:', event.url);
this.previousUrl = event.url;
});
}
loadPreviousRoute(){
let previous = this.routerService.getPreviousUrl();
if(previous)
this.routerService.router.navigateByUrl(previous);
}
}
When the application will first load, I will route it to 'home' page. There I provided a button to navigate to not-found page.
As you see in the above code of home component , I am setting the previous url in the router service which I can use in next component ( in this example it is 'not-found' component'
c) below are the html code snippets for both home and not-found components :
Home.component.html :
<hello name="{{ name }}"></hello>
<p>
Start editing to see some magic happen :)
</p>
<button type="button" (click)="takeMeToNotFoundPage()">Not FOUND!</button>
not-found.component.html:
<p>
<span style="background-color: red">NOT FOUND!!</span>
</p>
<button type="button" (click)="loadPreviousRoute()">Go Back to Previous Route</button>
Hope this will help you to solve your problem. If it will , please provide your feedback and upvote it accordingly as it will help others in future too.

how to pass form value to another component in angular

I want to show taxDetailsId in my child component Html page.
But when click submit button.
After click submit button then shows taxDetailsId in my child component Html page.
Parent Component
export class OnlinePaymentComponent implements OnInit {
HttpClient: any;
paymentForm: FormGroup = this.formBuilder.group({
taxDetailsId: ['', [Validators.required]]
});
constructor(
private formBuilder: FormBuilder,
private router: Router,
) {}
ngOnInit() {}
submitForm(): void {
if (!this.paymentForm.valid) {
this.router.navigate(['/home/online-payment/error']);
return;
}
}
}
Parent.Component.html
<form [formGroup]="paymentForm" (ngSubmit)="submitForm()">
<label>Tax Details Id</label>
<input type="text" formControlName="taxDetailsId" placeholder="Tax Details Id" />
<button>Pay Bill</button>
<form>
Child Component
export class OnlinePaymentErrorComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
Child.Component.html
<div>
<button [routerLink]="['/home/online-payment']" >Back Home</button>
</div>
you can try this pattern this.router.navigate(['/heroes', { id: heroId }]);
https://angular.io/guide/router
you can use angular #Input() decorator for it.
Child Component
import { Component, Input } from '#angular/core';
export class ChileComponent {
#Input() public taxDetailsId: number;
}
Child Component HTML
enter code here
<div>
{{ taxDetailsId }}
<button [routerLink]="['/home/online-payment']" >Back Home</button>
</div>
Parent Component HTML
<app-child-component [taxDetailsId]="taxDetailsId"> </app-child-component>
https://angular.io/guide/inputs-outputs
You can pass components around using Angular's InjectionToken.
First you start off by creating the token:
export const ONLINE_PAYMENT_REF = new InjectionToken<OnlinePaymentComponent>('OnlinePaymentComponent');
Next you add the token to one of the root components as a provider, in this case it is the OnlinePaymentComponent. This way everything that is a child of this component, and everything that is a child of those components, and so on; will have a reference to the main parent that we create here:
#Component({
selector: 'online-payment',
template: `
<online-payment-error></online-payment-error>
`,
providers: [
{
provide: ONLINE_PAYMENT_REF,
// Forwards the instance of OnlinePaymentComponent when injected into
// the child components constructor.
useExisting: forwardRef(() => OnlinePaymentComponent)
}
]
})
export class OnlinePaymentComponent {
message = 'I am the Online Payment Component';
}
Now that we have the main component setup, we can access it through the constructor of anything that is a child of OnlinePaymentComponent (no matter how deep it is).
#Component({
selector: 'online-payment-error',
template: `
<h2>Child</h2>
<strong>Parent Message:</strong> {{parentMessage}}
`
})
export class OnlinePaymentErrorComponent implements OnInit {
parentMessage = '';
constructor(
#Inject(ONLINE_PAYMENT_REF) private parent: OnlinePaymentComponent
) {}
ngOnInit() {
this.parentMessage = this.parent.message;
}
}
When all is said and done, you will see the following:
The pros of this method are that you don't have to bind values to the elements in the template, and if those components have components that need to reference the parent you wouldn't have to bind to those either. Since components look up the hierarchy till they find the first instance of the provider that we are looking for they will find the one in OnlinePaymentComponent.
This becomes very helpful when components get deeper and deeper into the parent component (say 5 levels deep), that means every time you would have to pass a reference to the template element 5 times, and if it changes or gets deeper you would have to update all the templates.
With this method we no longer need to update templates to pass data from one component to another component, we just request it in our constructor as seen in OnlinePaymentErrorComponent.
There are two simple ways:
Using query params (without routerLink).
Using Observables.
Using query params, you can use the router.navigate and pass the params you need (Id) along with the route.
eg: this.route.navigate(['yourroute/route', { tId: variableWithId }])
Using Observable, when you click on the button, use the same router navigate without params and pass the required data to an observable. On successful routing to the next page, get the resolved data from the observable.

Angular: pass single object within ngFor to router linked component

How do I pass the data binded information within the <a> tag (Within the volume-links.component.html ) to my page-view component when the link is clicked.
I want to pass that particular diary object to my page-view.
I've looked into parent and child component interaction but I don't think that is the proper way to do it. I've looked into communicating via a service but I do not know how that would work for a problem such as this.
volume-links.component.html
<ul class="navigation">
<li *ngFor="let d of diary">
<a id={{d.notebook_id}} routerLink="/page-view" routerLinkActive="active">Volume {{d.notebook_id}}, {{ d.date }}, {{ d.volume_id }}, Add MS {{ d.ms_id }}</a>
</li>
</ul>
volume-links.component.ts
import { Component, OnInit } from '#angular/core';
import { Http } from '#angular/http';
import { HttpClient, HttpClientModule } from '#angular/common/http';
import 'rxjs/add/operator/map'
#Component({
selector: 'app-volume-links',
templateUrl: './volume-links.component.html',
styleUrls: ['./volume-links.component.scss'],
//encapsulation: ViewEncapsulation.None
})
export class VolumeLinksComponent implements OnInit {
diary : String;
constructor(private http: HttpClient) { }
ngOnInit() {
this.http.get('/api/diaries').subscribe(data => {
this.diary = data["data"]["docs"];
console.log(this.diary);
})
}
}
You want to look at https://angular.io/guide/component-interaction
There are several methods / ways to achieve this and depending on your use case choose one.
I would define an Input property in VolumeLinksComponent and pass the diary object in there (that's the first part "Pass data from parent to child with input binding").
This would look something like:
<a *ngFor='let diary of diaries' (click)='chooseDiary(diary)'>
<my-diary-container [diary]='selectedDiary'></my-diary-container>
and that parent component of course needs a property 'selectedDiary' and a method:
chooseDiary(diary: Diary): void {
this.selectedDiary = diary;
}
But in your provided case it seems like you just need the specific id since you want to retrieve details from the api? In that case you could just define a route with the id and when the route is accessed ask an additional DiaryService to retrieve what you need.

How to reload the current Angular 2 Component

How can I reload the same component again in Angular 2?
Here is my code below:
import { Component, OnInit, ElementRef, Renderer } from '#angular/core';
import { Router, ActivatedRoute, Params } from '#angular/router';
import { productModel } from '../_models/index';
import { categoryListService } from '../_services/index';
#Component({
selector: 'app-product',
templateUrl: 'product.component.html',
styleUrls: ['product.component.css']
})
export class productComponent implements OnInit {
uidproduct: productModel;
param: number;
constructor(
private elementRef: ElementRef,
private route: ActivatedRoute,
private router: Router,
private categoryListService: categoryListService) { }
ngOnInit() {
this.route.params.subscribe(product => {
console.log('logging sub product obj', product);
});
this.uidproduct = JSON.parse(sessionStorage.getItem('product'));
var s = document.createElement("script");
s.type = "text/javascript";
s.src = "http://this/external/script/needs/to/be/loaded/each/time.js";
this.elementRef.nativeElement.appendChild(s);
}
nextproduct(){
let i = this.uidproduct.order;
this.categoryListService.findNextproduct(this.uidproduct);
this.param = ++i;
this.router.navigate([`/product/${this.param}`]);
}
}
nextproduct() is bound to a click event in the template.
The uidproduct is a JSON object that has a number of properties and i'm updating the DOM with {{uidproduct.classname}}
I'm using this in the template like this:
<div id="selected-product" class="{{uidproduct.classname}}">
When I click the <button (click)="nextproduct()"> it will change the class property in the DOM but I need to reload the component for the external script to have effect.
You can use *ngIf to re-render the content of a template:
#Component({
selector: '...',
template: `
<ng-container *ngIf="!rerender">
template content here
</ng-container>`
})
export class MyComponent {
rerender = false;
constructor(private cdRef:ChangeDetectorRef){}
doRerender() {
this.rerender = true;
this.cdRef.detectChanges();
this.rerender = false;
}
}
I don't understand why you need to reload the component. If you're binding to the various fields of uidproduct, then reloading that should refresh the values shown in the component. So reloading the component does nothing but add overhead.
If there is a terrific reason not mentioned here why you think you still need to do this, then here is what you do:
Navigate to another (possibly blank) component.
Navigate directly back.
The problem is that you need to wait for the first navigation to finish before doing the second one.
In your component, import NavigationEnd:
import { Router, NavigationEnd } from '#angular/router';
And then subscribe to it in your constructor:
constructor(private thingService: ThisThingService, private router: Router) {
router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
if (event.url === '/blank') {
this.router.navigate(['product']);
}
}
});
Notice that I wait for NavigationEnd to happen and then check to see I was routing to my blank component. If it is the blank component's path, I navigate back to the product. If you really need to pass that ID, just store it on your object and add it here.
Instead of routing to your product page in nextproduct(), navigate to blank.
this.router.navigate(['blank']);
And that should reload your component perfectly fine.
The problem I intentionally left in for simplicity, is that the subscribe call in the constructor will execute for every reload. So as an exercise to the reader, take it out of the constructor and create a nice service for it, or move it to the constructor of your app component, or maybe to your routing module or wherever makes sense to you.

Angular2 component doesn't detect routing parameter updates (Router 3.0)

I've got a small Plunk I'm using for playing around with the new Router 3.0 alpha currently available in Angular 2. It works well in general, but the issue is that once I click on a link that routes to the 'detail' component with a particular ID, it never changes when I click on a different link with a different ID. The component is never being reinstantiated, so it only ever shows what it was passed the very first time it is loaded.
Here's the component in question:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ContactsService } from './contacts.service';
#Component({
selector: 'contacts-detail',
template: `
<h2>{{contact.name}}</h2>
`
})
export class ContactsDetailComponent implements OnInit {
constructor(private contactsService: ContactsService, private route: ActivatedRoute) {
}
ngOnInit() {
this.contact = this.contactsService.getContact(this.route.snapshot.params.id);
console.log('Fetching user', this.route.snapshot.params.id);
}
}
Here is the Plunk demonstrating the problem. Click on one author name and then another to see it not change.
In your ContactsDetailComponent, change the OnInit to this:
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
let id = +params['id'];
this.contact = this.contactsService.getContact(id);
});
}
Worked for me in your Plunk.
There appear to be multiple lifeCycle hooks that could possibly be used for this. I managed to get the desired behavior using the DoCheck interface and implementing the associated ngDoCheck() method in the component class, as seen below.
import { Component, DoCheck } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ContactsService } from './contacts.service';
#Component({
selector: 'contacts-detail',
template: `
<h2>{{contact.name}}</h2>
`
})
export class ContactsDetailComponent implements AfterViewChecked, DoCheck {
constructor(private contactsService: ContactsService, private route: ActivatedRoute) {
}
ngDoCheck() {
this.contact = this.contactsService.getContact(this.route.snapshot.params.id);
}
}
Here's a plunk with the updated code.
I'm not convinced this is the best/correct lifecycle hook to use, though. Perhaps there is some sort of hook available from the Router that would serve this better.
Another way to do this:
ngOnInit() {
this.route.params.forEach((params: Params) => {
let id = +params['id'];
this.contact = this.contactsService.getContact(id);
});
}
Here retrieve the route params from an Observable. The advantage of using an Observable over Snapshot is to reuse the component without instantiating it again. Looks like this is the recommended way of doing this as per Angular 2.0 final documentation.

Categories

Resources