Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the route parameter out of ParamMap and assign it to a property

I am running Angular 6 with RxJs 6.

I am trying to get the value out of paramMap and assign it to a property. The route looks like this:

{
    path: 'post/:postName',
    component: PostComponent
}

The post component currently looks like this:

import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { switchMap } from 'rxjs/operators';
import { Observable, of } from 'rxjs';
import * as matter from 'yaml-front-matter';
import { Post } from '../../models/post';

@Component({
  selector: 'blog-post',
  templateUrl: './post.component.html',
  styleUrls: ['./post.component.css'],
  encapsulation: ViewEncapsulation.Emulated
})
export class PostComponent implements OnInit {
  created: Date;
  postName = '';
  markdown = '';
  title = '';
  loading = true;
  author = '';

  constructor(private route: ActivatedRoute) { }

  async ngOnInit () {
    this.route.paramMap.pipe(
      switchMap((params: ParamMap) =>
        of(params.get('postName'))
      )
    ).subscribe(d => this.postName = d);

    const file = await fetch(`/_posts/${this.postName}.md`);
    const text = await file.text();
    const content = text.trim();
    this.parseFrontMatter(content);
  }

  parseFrontMatter (content: string) {
    const frontMatter: Post = matter.loadFront(content);
    this.title = frontMatter.title;
    this.author = frontMatter.author;
    this.created = frontMatter.created;
    this.markdown = frontMatter.__content;
    this.loading = false;
  }
}

In the ngOnInit is the code for getting the value out of the paramMap. This code works unless you try to route back to the post component with a different parameter.

This can be observed at Devbin.io.

  1. Navigate to the posts page
  2. Click on the only post
  3. Now try to navigate to the about page which is just another post.
  4. Notice that it will not route to the About page even though the Url is updated.

The full source can be found on GitHub

All of the documentation and other Stack Overflow answers always assume you want to call a service in the switchMap function. The official documentation is here

My question is how do I get the route parameter and assign it to a property and keep navigation working while navigating back to the same route?

like image 581
Mizzle-Mo Avatar asked Jun 05 '18 16:06

Mizzle-Mo


2 Answers

This is because ngOnInit doesn't get called when you route to the same component. In your case /post/Angular-6-Router-How-To-Get-Route-Parameters and /post/About are triggering the same component: post.component.

So to make this work you could call a function inside subscribe in paramMap.pipe.

You can do something like this:

ngOnInit() {
    this.route.paramMap.pipe(
      switchMap((params: ParamMap) =>
        of(params.get('postName'))
      )
    ).subscribe((d) => {
      this.postName = d;
      this.postInit();
    });

  }

  async postInit() {
    const file = await fetch(`/_posts/${this.postName}.md`);
    const text = await file.text();
    const content = text.trim();
    this.parseFrontMatter(content);
  }

  parseFrontMatter(content: string) {
    const frontMatter: Post = matter.loadFront(content);
    this.title = frontMatter.title;
    this.author = frontMatter.author;
    this.created = frontMatter.created;
    this.markdown = frontMatter.__content;
    this.loading = false;
  }
like image 84
Gregor Ojstersek Avatar answered Oct 18 '22 09:10

Gregor Ojstersek


So I have cloned the repo and fixed the issue.

Replace this:

   async ngOnInit () {
        this.route.paramMap.pipe(
          switchMap((params: ParamMap) =>
            of(params.get('postName'))
          )
        ).subscribe(d => this.postName = d);

        const file = await fetch(`/_posts/${this.postName}.md`);
        const text = await file.text();
        const content = text.trim();
        this.parseFrontMatter(content);
      }

with the following:

  ngOnInit() {
    this.route.paramMap.pipe(
      switchMap((params: ParamMap) =>
        of(params.get('postName'))
      )
    ).subscribe(d => {
      this.postName = d;

      fetch(`/_posts/${this.postName}.md`)
        .then(data => data.text()
          .then(text => {
            const content = text.trim();
            this.parseFrontMatter(content);
          }));
    });
  }

Explanation: You were fetching the posts only when the component was initialized since your file fetch code was outside of the Observable subscribe method. I moved in and chained the promises and then passed the final value to your function parseFrontMatter

like image 36
Jean Guzman Avatar answered Oct 18 '22 09:10

Jean Guzman