Updating a specific section of the DOM while altering the URL in Angular 2

I am in the process of developing a web application that showcases news articles. My goal is to create a single-page app where users can view a list of article titles and then click on a title to read the full content without reloading the entire page.

I have successfully implemented this feature, but now I want to update the URL dynamically when an article is clicked, without refreshing the entire page. Despite following the guidance on child routing components and other resources, I keep encountering an error message in the console:

Cannot find primary outlet to load ''

Here is the code I have been working with:

app/app.routes.ts :

import { provideRouter, RouterConfig }  from '@angular/router';
import { articlesRoutes } from './articles/articles.routes';
import { ArticlesComponent } from "./articles/articles.component";

const routes: RouterConfig = [
    ...articlesRoutes,
];

export const appRouterProviders = [
    provideRouter(routes)
];

app/articles/articles.routes.ts :

import {ArticlesComponent} from "./articles.component";
import {RouterConfig} from "@angular/router";
import {ArticleDetailComponent} from "./detail/article-detail.component";

export const articlesRoutes: RouterConfig = [
    {
        path: '',
        component: ArticlesComponent,
        children: [
            {
                path: 'detail/:id',  component: ArticleDetailComponent
            },
        ]
    }
];

app/articles/articles.component.html

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ArticleService } from "./shared/article.service";
import { Article } from "./shared/article.model";
import { ArticleDetailComponent } from "./detail/article-detail.component";
import { ROUTER_DIRECTIVES }  from '@angular/router';

@Component({
    selector: 'fcso-articles',
    templateUrl: 'app/articles/articles.component.html',
    directives: [ROUTER_DIRECTIVES],
    providers: [ArticleService],
})
export class ArticlesComponent implements OnInit {
    articles: Article[] = [];
    articleOpened: Article = null;
    error: any;

    constructor(
        private articleService: ArticleService,
        private router: Router) {}

    getArticles() {
        this.articleService
            .getArticles()
            .then(articles => this.articles = articles)
            .catch(error => this.error = error);
    }

    ngOnInit() {
        this.getArticles();
    }

    //function changing the hidden article and redirecting to URL
    gotoDetail(article: Article) {
        this.articleOpened = article;
        this.router.navigate(['', '/detail', this.articleOpened.id]);
    }
}

index.html

    <body>
      <!-- the main content (from app.component.ts) is not loaded from router -->
      <fcso-main>
        Loading...
      </fcso-main>
    </body>

app/app.component.html

<div class="container">
    <div class="col-xs-12 col-md-8 col-md-offset-2 fcso-no-padding">
        <div class="panel panel-default">
            <div class="panel-body">
                <h1 class="text-center">Title</h1>
            </div>
        </div>
        <!-- this router outlet should show articlesComponent -->
        <router-outlet></router-outlet>
    </div>
</div>

app/articles/articles.component.html

<div class="panel panel-default" *ngFor="let article of articles">
    <div class="panel-heading fcso-panel-heading" *ngIf="article !== articleOpened" >
        <h3 class="text-center fcso-open-panel" (click)="gotoDetail(article)">{{article.title}}</h3>
    </div>
    <!-- router-outler is hidden to boost angular perfs and avoid troubles -->
    <router-outlet *ngIf="article === articleOpened"></router-outlet>
</div>

I have attempted to consolidate the content of articles.routes.ts directly into app.routes.ts without success. While the list of articles loads successfully, the child content does not load, resulting in the following error message:

Cannot find primary outlet to load 'ArticleDetailComponent'

Even utilizing routerConfig in the .component.ts file did not yield a different outcome. I am unsure of the steps required to display the child (ArticleDetailComponent) in the designated location upon clicking the title and modifying the URL. Any guidance on resolving this issue would be appreciated.

Answer №1

  • When using *ngFor, make sure to only include one <router-outlet> without a name. Any additional <router-outlet> elements must have distinct names to avoid creating multiple nameless outlets, which is not allowed.

  • Don't forget to use the spread operator when defining routes:

const routes: RouterConfig = [
  ... articlesRoutes,
];
  • Make sure to include a default route for child routes.
export const articlesRoutes: RouterConfig = [
  { path: '', component: ArticlesComponent, children: [
    { path: '', redirectTo: 'detail/01', pathMatch: 'full'}, /* required */
    { path: 'detail/:id',  component: ArticleDetailComponent },
  ]}
];

Check out this Plunker example for reference.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using Lerna with Docker for Next.js and GraphQL applications

Currently, I am working with lerna and everything runs smoothly locally. However, when I attempt to build the image and operate it through Docker, it does not function as expected. FROM node:16-alpine3.11 ENV NODE_ENV=production COPY . /app WORKDIR /app R ...

Angular 4: Utilizing a class with a constructor to create an http Observable model

Within my application, I have a Class model that is defined with a constructor. Here is an example: export class Movie { title: string; posterURL: string; description: string; public constructor(cfg: Partial<Movie>) { Object ...

What sets apart module imports in JavaScript and Typescript?

Exploring the realm of Shadow DOM and Custom elements, I've encountered an interesting discrepancy between how JavaScript (JS) and TypeScript (TS) handle modular imports. Am I missing something here? My primary JS file has this structure... // impor ...

Encountered a module build error while upgrading Angular Project from version 14 to 15

When attempting to run my project, an error is displayed. ./src/styles.scss?ngGlobalStyle - Error: Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js): HookWebpackError: Module build failed (from ./node_modules/sass-loader/dist ...

The constructor for this property 'formGroup' does not have an initializer and is not explicitly assigned a value

Hey there! I am still learning angular and encountering an issue that says "Property 'formGroup' has no initializer and is not definitely assigned in the constructor" even though I have declared formGroup. Not sure why this error is popping up. i ...

Save JSON Tree data in the Database

Given a tree structure JSON, I am tasked with creating an API to insert all the data into a database at once. The organization entities can have multiple parents and children relationships. An example of the JSON data: { "org_name": "orga ...

Retrieving a specific item using its ID from a JSON file with Ionic 5

Newcomer's query For multiple Ionic pages, I require fetching a specific item by ID from a centralized JSON file. The structure of my JSON data is as follows: { "items": [ { "id":"0", "link&q ...

Is it possible to make my Toggle/Click event refresh the entire component every time it is clicked?

I'm trying to implement a toggle function to show/hide a specific DIV and dynamically change the button text based on the current state in React Hooks. However, every time I click on it, the entire page seems to re-render in Next.js. I'm not enti ...

Using a Typescript-specific type within a switch case statement

I'm currently working on a function that, when given an enum value, should return a specific type. I am facing an issue where Typescript does not seem to recognize the properties inside switch and if statements. interface X { x: string; } interface ...

What is the best way to manage a custom child event that is triggered using this.$emit in a parent component, specifically within the <script> section of the .vue file?

In our project, we're utilizing vue and typescript, which means that our .vue files are structured very similarly to the layout outlined in this blogpost. One of our child components is emitting a custom event called changeType. I'd like to trig ...

Can the inclusion of additional parameters compromise the type safety in TypeScript?

For demonstration purposes, let's consider this example: (playground) type F0 = (x?: string) => void type F1 = () => void type F2 = (x: number) => void const f0: F0 = (x) => console.log(x, typeof(x)) const f1: F1 = f0 const f2: F2 = f1 f ...

What is the best way to exclude multiple properties from an object in JavaScript?

Having two methods that return Pick<T, K> and Omit<T, K> types where Omit is defined as type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>, I am facing difficulty in removing multiple properties from an object. Th ...

The element ion-view is not recognized

Here is the content I have for my Ionic page: <ion-content padding class = ""view-content"> <form> [form content here...] </form> </ion-content> This is the CSS code from my file: .view-content { backg ...

Sending information from the parent component to the child Bootstrap Modal in Angular 6

As a newcomer to Angular 6, I am facing challenges with passing data between components. I am trying to launch a child component bootstrap modal from the parent modal and need to pass a string parameter to the child modal component. Additionally, I want t ...

Unlock the full potential of ts-transformer-keys in your Vue application

note: After spending countless hours on this, I finally had a breakthrough today. It turns out that changing transpileOnly to false made all the difference: chainWebpack: config => { const getCustomTransformers = program => ({ before: [ ...

the hidden input's value is null

I am encountering an issue with a hidden input in this form. When I submit the form to my API, the value of the input is empty. Isbn and packId are both properties of a book model. However, for some reason, the value of packId is coming out as empty. & ...

Unable to connect to web3 object using typescript and ethereum

Embarking on a fresh project with Angular 2 and TypeScript, I kicked things off by using the command: ng new myProject Next, I integrated web3 (for Ethereum) into the project through: npm install web3 To ensure proper integration, I included the follow ...

What seems to be the issue with the useState hook in my React application - is it not functioning as

Currently, I am engrossed in a project where I am crafting a Select component using a newfound design pattern. The execution looks flawless, but there seems to be an issue as the useState function doesn't seem to be functioning properly. As a newcomer ...

Leveraging the Angular Material 2 table component to showcase data fetched from the backend system according to the user's present location

This question is similar to another one about how to get the current location in Typescript and pass it to a backend, which was answered by libertyernie. However, this time I need help with integrating the current location into an Angular Material 2 table ...

How to utilize Enzyme to call a React prop in TypeScript

I'm currently in the process of converting my Jest tests from Enzyme to TypeScript, and I've come across a specific case that I'm unsure how to resolve. Essentially, I'm attempting to invoke a function passed as a prop to a sub-componen ...