0

I have the following component:

nav.component.ts

import { Component, OnInit } from '@angular/core';
import { ApiService } from '../api.service';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-nav',
  templateUrl: './nav.component.html',
  styleUrls: ['./nav.component.css'],
  providers: [ApiService]
})
export class NavComponent implements OnInit {
menus;
id;
    constructor(private api: ApiService, private route: ActivatedRoute) { }

    ngOnInit() {
    this.route.queryParams.subscribe(queryParams => { this.id = queryParams.id });
    this.api.getMenus().subscribe(res => {this.menus = res});

    this.route.params.subscribe(routeParams => {
    this.set_id(routeParams.id)
    })
  }

  set_id(id){
    this.id = id
    console.log(this.id)
  }
}

In this component a menu is being loaded and a variable is being set from a route.param coming from the following link:

nav.component.html

<div *ngFor="let menu of menus; index as id">
    <ul>
        <li><a routerLink ="content/{{id}}" (click)="set_id(id)">{{menu['course-lesson-name']}}</a></li>
    </ul>
</div>

The app loads just fine, but the content only updates a single time. After any single menu item is clicked, the content is shown, but then I lose functionality in that any subsequent click loads nothing. If I manually manipulate the url in the browser for each link, the content loads as needed so Im assuming something is wrong with the linking. BTW, the id is being set as required and is logging to the console, but doesnt reload the content as needed based on its value.

Here is the router if needed:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ContentComponent } from './content/content.component';
import { HomeComponent } from './home/home.component';

const routes: Routes = [
  {path: 'content/:id', component: ContentComponent},
  {path: '', component: HomeComponent}
];
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

The id should be stored and passed to the content.component.ts so getContent() can use it to parse the correct content element from the response:

content.component.ts

import { Component, OnInit, OnChanges, Input } from '@angular/core';
import { ApiService } from '../api.service';
import { RouterLink, ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-content',
  templateUrl: './content.component.html',
  styleUrls: ['./content.component.css'],
  providers: [ApiService]
})
export class ContentComponent implements OnInit {
content;
id;
  constructor(private api: ApiService, private route: ActivatedRoute) { }

  ngOnInit(){
    this.route.queryParams.subscribe(queryParams => { this.id = queryParams.id });
    this.api.getContent().subscribe((res : any[]) => {this.content = res[this.id]})
    this.api.getContent().subscribe((res) => {this.content = res[this.id]})
  }

}
DataGuy
  • 1,695
  • 4
  • 22
  • 38

2 Answers2

1

Yes, this is because your component isn't reloaded when a param changes in the URL. There are some pretty lengthy (and heated) GitHub issues over this on the angular GitHub. Essentially, you need to do this:

  1. Inject ActivatedRoute in your component (this._route below)
  2. Subscribe to the queryParams and do what you need to

Example:

this._route.queryParams.subscribe( q => {
    // Do something...
} );

The typical pattern I follow for this type of thing is to encapsulate whatever logic you need to run when the param changes into a function. Then, run that function in your ngOnInit and then right below it, subscribe to the queryParams so you can react to any updates (thus calling the same function that handled that).

mwilson
  • 12,295
  • 7
  • 55
  • 95
  • Thanks for the advice. I updated my nav component above - still no results. – DataGuy Mar 06 '20 at 00:29
  • Can you update your code. The proposed solution I outlined above is pretty much the only option. There's some hacks to force angular to refresh on param change, but they are much more involved. – mwilson Mar 06 '20 at 00:39
  • I have updated my nav.component above. Is there a better framework for this type of architecture? – DataGuy Mar 06 '20 at 00:40
  • You could try out some of the other ones like Vue.js, React or whatever, but I think you'll find that they bring lots of other issues. Meaning that Angular is a framework and react is a library and vue a micro-framework. They aren't as full-featured as angular is. In your code above, are you seeing the console.log messages or any exceptions in the console? – mwilson Mar 06 '20 at 00:43
  • I am seeing (this.id) in set_id(id) being logged successfully and with the correct id value each time I click on a link. – DataGuy Mar 06 '20 at 00:46
  • Yea, then that means it's working. It's not clear what you're expecting to happen once you have the new id, however. If you're using ngModel or some other data-binding setup, you'll need to bind the new Id to the model. – mwilson Mar 06 '20 at 00:50
  • The id is supposed to be used by the content component (Ive added it and a description above). – DataGuy Mar 06 '20 at 00:54
  • Yea, I see. It looks like your issue is that you need to move the code that does something with the Id inside your `queryParams.subscribe` – mwilson Mar 06 '20 at 00:58
  • 1
    Like this: `this.route.queryParams.subscribe(queryParams => { this.id = queryParams.id; this.api.getContent().subscribe((res : any[]) => {this.content = res[this.id]}) this.api.getContent().subscribe((res) => {this.content = res[this.id]}) });` – mwilson Mar 06 '20 at 01:00
  • Thanks - still doing the same thing. Would I be better off just using a single component and two divs? At least I could keep everything contained. It seems like that the main focus behind the Angular component structure anyway. – DataGuy Mar 06 '20 at 01:11
  • Wow. I missed a big one there. I thought this was one single component. So, yes, merging them into one component with two divs would work (if it makes sense to merge them). If you don't want to do that, you can setup a Service with an EventEmitter that will emit data to your components. That would be the angular way to do it, at least. This isn't to say that you can't do it without the service, but you would need some kind of parent component to control the two child components (would require an @Input binding on each child component) – mwilson Mar 06 '20 at 01:44
  • 1
    Many thanks @mwilson - Im off to try a single component solution! – DataGuy Mar 06 '20 at 01:49
0

You are not telling the router to do anything after the id is selected... For the click event in the nav.component.ts you should navigate to the selected id route.

set_id(id){
    this.router.navigate(['content/' + id ]);
}