Is it Possible for Angular Layout Components to Render Content Correctly even with Deeply Nested ng-container Elements?

Within my Angular application, I have designed a layout component featuring two columns using CSS. Within this setup, placeholders for the aside and main content are defined utilizing ng-content.

The data for both the aside and main sections is fetched from the server. A loading flag is set during the loading phase to signify that data retrieval is in progress. Once successful data retrieval occurs, an isSuccess flag is returned alongside the response from the observable.

To replicate this process, I've crafted a mock data$ observable with the help of the RxJS of operator. This observable includes a delay to mirror the asynchronous nature of data retrieval.

An issue has surfaced where the layout fails to properly render the aside and main content. This problem arises due to the fact that the ng-content directive anticipates the attributes aside and main directly, but they exist deeply nested inside ng-container.

The question at hand is whether it's feasible to modify this setup to correctly showcase the content?

Below is the revised code snippet:

import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { delay, of, startWith } from 'rxjs';
import 'zone.js';

@Component({
  selector: 'layout',
  standalone: true,
  template: `
    <div class="container">
      <aside>
        <ng-content select="[aside]"></ng-content>
      </aside>
      <main>
        <ng-content select="[main]"></ng-content>
      </main>
    </div>
  `,
  styles: [`.container {display:grid; grid-template-columns: 1fr 2fr} `],
})
export class LayoutComponent {}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [LayoutComponent, CommonModule],
  template: `
    <layout>
      <ng-container *ngIf="data$ | async as result">
        <div *ngIf="result.loading">loading...</div>
        <ng-container *ngIf="result.isSuccess">
          <div aside>aside content: {{ result.response.aside | json }}</div>
          <div main>main content: {{ result.response.main | json }}</div>
        </ng-container>
      </ng-container>
    </layout>
  `,
})
export class App {
  data$ = of<any>({ 
    response: { aside: [1, 2, 3, 4], main: [10, 12, 13, 14] },
    isSuccess: true,
  }).pipe(delay(1 * 1000), startWith({ loading: true }));
}

bootstrapApplication(App);

Access the code at this link: StackBlitz

Answer №1

When using ng-content tags, it's important for them to be at the top level in order to be visible for content projection.

I've made changes to the HTML structure so that we can retrieve data and display content conditionally based on specific conditions. By leveraging if else (ng-template), we can incorporate a loading section as needed!

Feel free to adjust the code according to your preferences and requirements!

Here is the full revised code:

import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { delay, of, startWith } from 'rxjs';
import { endWith } from 'rxjs/operators';
import 'zone.js';
console.clear();

@Component({
  selector: 'layout',
  standalone: true,
  template: `
    <div class="container">
      <aside>
        <ng-content select="[aside]"></ng-content>
      </aside>
      <main>
        <ng-content select="[main]"></ng-content>
      </main>
    </div>
  `,
  styles: [`.container {display:grid; grid-template-columns: 1fr 2fr} `],
})
export class LayoutComponent {}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [LayoutComponent, CommonModule],
  template: `
    <ng-container *ngIf="data$ | async as result">
      <layout *ngIf="result.isSuccess;else loading">
        <div aside>aside content: {{ result.response.aside | json }}</div>
        <div main>main content: {{ result.response.main | json }}</div>
      </layout>
    </ng-container>
    <ng-template #loading>
      <div>loading...</div>
    </ng-template>
  `,
})
export class App {
  name = 'Angular';

  data$ = of<any>({
    response: { aside: [1, 2, 3, 4], main: [10, 12, 13, 14] },
    isSuccess: true,
  }).pipe(delay(1 * 1000), startWith({ loading: true }));
}

bootstrapApplication(App);

Check out the Stackblitz Demo

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

reactjs: disable a specific eslint rule

I'm trying to figure out how to disable the "no-unused-vars" rule specifically for TypeScript interfaces. Here's a code snippet where I'm getting a warning: export type IKeoTableColumn<T> = { id: T; align: 'left' | ' ...

Enhancing the appearance of dropdown menus for WooCommerce Variable products with custom CSS styling

I currently have a Wordpress website with WooCommerce and several variable products. Each product's variation has a dropdown menu that displays different options when clicked. My main concern is how to customize the appearance of these dropdown menus ...

Angular Bootstrap Datepicker provides us with a date object, but I need it in the Date format

My desired date format is "Wed Aug 07 2019 16:42:07 GMT+0530 (India Standard Time)", but instead I am receiving { year: 1789, month: 7, day: 14 } from ngbDatepicker. Any assistance on resolving this issue would be greatly appreciated. ...

Attempting to transfer information between components via a shared service

Currently, I am utilizing a service to transfer data between components. The code snippet for my component is as follows: constructor(private psService: ProjectShipmentService, private pdComp: ProjectDetailsComponent) { } ngOnInit() { this.psSe ...

Is it feasible to broaden an interface in Typescript without including a specific type?

import React from "react"; interface a_to_e { a?: string; b?: string; c?: string; d?: string; e?: string; } interface a_to_e_without_c extends a_to_e { // I want to include properties a~e except for c } function Child(props: a_to_e_without_c ...

The error message shows: "Unable to retrieve property 'opts' from an undefined source in google-auth-library"

My current task involves retrieving tokens from Google for a specific user. Here is the code snippet I'm using: const util = require('util'); return util.promisify(oauth2Client.getToken, {context: oauth2Client})(googleToken).then(tokens ...

What is the process for updating information in Vue.js?

I need assistance with displaying the updated data in a modal. When I trigger the testing(data) function through a click event, the data appears correctly within the function. However, the template does not update and still shows the previous data. How can ...

I am seeking to showcase an image in a window, and upon the image being clicked, execute the code in a separate window

I am looking to showcase the image provided below. <img src="http://gfx.myview.com/MyView/skins/livesample/image/livesample.gif" alt="" border="0"><a/> Once the image is clicked, I want it to execute the following code. How can I ensure that ...

Utilizing AngularJS to show content based on regular expressions using ng-show

With two images available, I need to display one image at a time based on an input regex pattern. Here is the code snippet: <input type="password" ng-model="password" placeholder="Enter Password"/> <img src="../close.png" ng-show="password != [ ...

What is the method used by Vue.js to establish observers in computed properties?

Consider this scenario: computed: { greeting() { const state = this.$store.state; if (state.name === 'Joe') { return 'Hey, Joe'; } else { return 'Hello, ' + state.name; } } } Which object(s) w ...

I have installed npm globally, but for some reason, I keep getting an error prompting me to reinstall it for every new Angular

Recently, I started delving into Angular 4. Following a tutorial, I installed nodejs, then the angular 4 cli, and created my project to begin working. Everything seemed fine until I tried running a local server using ng serve --open, which resulted in the ...

Inspecting an unspecified generic parameter for absence yields T & ({} | null)

Recently, I came across a perplexing issue while responding to this specific inquiry. It pertains to a scenario where a generic function's parameter is optional and its type involves the use of generics. Consider the following function structure: func ...

Using Angular 2, you can pass an object as a parameter to a function

Is there a way to pass an object as a parameter in the DOM on this forum? Within my HTML code, I have the following: <div class="list-items"> <ul> <li *ngFor="let i of item"> <span (click)="onAdd({{newUser.us ...

Testing Angular Components with setInterval FunctionTrying out unit tests in Angular

I'm struggling to figure out how to write unit tests for setInterval in my .component.ts file. Here is the function I have: startTimer() { this.showResend = false; this.otpError = false; this.time = 60; this.interval = setInterval(() => { this.ti ...

Tips for creating multiple files using nodejs and express

I am currently working on developing a personalized code editor that consists of 3 textareas: html, css, and javascript. The objective is to save the data from each textarea into individual files. With the help of express and nodejs, I have successfully m ...

Video is not visible in safari browser initially, but becomes visible only after scrolling for a little while

Having issues with a video not displaying correctly in Safari? The problem is that the video only becomes visible after scrolling the browser window. Want to see an example of this issue in action? Click here and select the red bag. Check out the code sni ...

Conceal HTML elements from the bottom as new content is being added dynamically

I am currently working on a comments feed feature. By default, only the first four comments are displayed, with an option to show more when clicking the "show more" anchor. The issue I'm facing is that if new comments are dynamically added, the CSS hi ...

Can you explain the concept of Cross-origin requests?

My JavaScript application is designed to detect single, double right, and double left clicks. A single click triggers an asynchronous request to the HTTP server, while the rest are intended to change the user interface on the client side. However, I am str ...

Exclude the initial argument from functions listed within a JSON structure

Is there a way to create a generic type that reflects a JSON object structure, but excludes the first argument from functions while maintaining the types of other arguments? type InputType<T> = { increment: (state: T) => T, add: (state: T, cou ...

What is the process for assigning a function and its arguments as a callback in programming?

Here is a code snippet for your consideration: $scope.delete=function(){ foo('x',3); }; How can we improve the clarity of this code snippet when the callback function contains only one line that calls another function? It's important ...