Navigate directly to the designated Expansion Panel within the Material Accordion once the Component has finished loading

We have encountered an issue where we are attempting to scroll to a specific <mat-expansion-panel> item within a <mat-accordion>. The problem arises because the ngAfterViewInit() is triggered before the accordion and its panels are fully loaded. This causes the scrollIntoView() function to be called while the accordions are still loading, resulting in changes to the page size and leading to our scroll operation taking us to the incorrect position on the page.

We have explored using other lifecycle hooks, but none have proven effective as they are all invoked too early. If anyone has any suggestions or best practices for resolving this issue, we would greatly appreciate it.

Our code is straightforward as we are trying to implement something basic:

landingpage.component.html

<mat-accordion>
   <mat-expansion-panel id="pangel-1">
    <mat-expansion-panel-header>
      <mat-panel-title>Lorem ipsum</mat-panel-title>
    </mat-expansion-panel-header>
    <p>
      Lorem ipsum dolor sit amet,
      consetetur sadipscing elitr,
      sed diam nonumy eirmod tempor invidunt...
    </p>
  </mat-expansion-panel>

  [ ... ] // more panels

</mat-accordion>

landingpage.component.ts

ngAfterViewInit() {
  this.scroll("panel-1");
}

scroll(id) {
  console.log(`scrolling to ${id}`);
  let el = document.getElementById(id);
  el.scrollIntoView();
}

Answer №1

Check out this demonstration on StackBlitz featuring Angular Material 7. Your approach is effective, but it's important to ensure that the page has enough content for the panel to appear at the top, and double-check that you've correctly spelled the panel's id.

Answer №2

When I interact with a specific element, it triggers the opening of a side navigation panel that occupies half of the screen. To handle this functionality, I have included the following logic within the click function:

if ($event) {
    setTimeout(() => $event.target.scrollIntoView({behavior: 'smooth', block: 'end'}), 1);
}

In your scenario, you can associate this behavior with the afterExpand event on the mat-expansion-panel and execute the same logic.

Answer №3

In encountering a similar issue, I devised a simple and elegant solution that I believe is worth sharing to potentially assist others and build upon existing solutions. My approach involved triggering a scroll into view after the state changed and the component mounted, while also managing timeouts for animations and scroll functionality. The key was to activate the scroll only if the currently active panel (with expanded state true) matched the currently rendered panel. I aimed to ensure all previous accordions remained expanded without closing others.

import React, { useRef, useEffect } from 'react';
import { withStyles, makeStyles } from '@material-ui/core/styles';
import MuiAccordion from '@material-ui/core/Accordion';
import MuiAccordionDetails from '@material-ui/core/AccordionDetails';
import MuiAccordionSummary from '@material-ui/core/AccordionSummary';

const ACCORDIAN_MIN_HEIGHT = 50;
const ACCORDIAN_V_MARGIN = 20;

const Accordion = withStyles((theme) => ({
    root: {
        margin: `${ACCORDIAN_V_MARGIN}px 0`,

      '&:not(:last-child)': {
        borderBottom: 0,
      },
      '&:before': {
        display: 'none',
      },
      '&$expanded': {
        margin: `${ACCORDIAN_V_MARGIN}px 0`,
      },
    },
}))(MuiAccordion);
  
const AccordionSummary = withStyles((theme) => ({
    root: {
        minHeight: ACCORDIAN_MIN_HEIGHT,
        pointerEvents: 'none',
        '&$expanded': {
        minHeight: ACCORDIAN_MIN_HEIGHT,
        },
    },
}))(MuiAccordionSummary);

const AccordionDetails = withStyles((theme) => ({
    root: {
        padding: theme.spacing(2),
    },
}))(MuiAccordionDetails);

// Additional styles
const useStyles = makeStyles((theme) => ({
    accordianContent: {
        '& .MuiAccordionSummary-content' :{
            fontFamily: 'Gilroy',
            fontSize: '24px',
            transition: 'font-size 1s ease',
            margin: `${ACCORDIAN_V_MARGIN}px 0`,
        },
        '& .MuiAccordionSummary-content.Mui-expanded': {
            fontSize: '35px',
        },

    },
}));

export default function AccordianSection(props) {
      
    const classes = useStyles();
    const thisAccordianRef = useRef(props.panel)

    const scrollQuestionIntoView = () => {
      setTimeout(() => thisAccordianRef.current.scrollIntoView({ behavior: 'smooth',block: "start", inline: "start" }),100)      
  }

  useEffect(() => {
    if (props.activeFormStep===props.panel) {
      scrollQuestionIntoView()
    }
  }, [props.activeFormStep])

      return (
          <>
            <Accordion ref={thisAccordianRef} expanded={props.expanded} 
                TransitionProps={{
                  timeout: 50
                }}
            >;
                <AccordionSummary
                expandIcon={null}
                className={classes.accordianContent}
               >
                        {props.sectionHeading}
                </AccordionSummary>;
                <AccordionDetails>
                    {props.children}
                </AccordionDetails>;
            </Accordion>;              
          </>
      )
  }

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

Expanding the functionality of your Angular2 application on the fly post-Bootstrap

Angular 1 required all Services and Directives to be included in the Angular Application before the Bootstrap Phase. However, Angular 2 introduces a new feature called hierarchical Injectors. How do hierarchical Injectors allow for the addition of Servic ...

Is there a way to determine which response corresponds to each request when hitting the same endpoint multiple times?

When making multiple requests to an API endpoint, how can I differentiate the response for each specific request? For example, if a user clicks the upload button three times, how can I track each request individually? ...

When you call Subscribe(), you will receive the output "complete: () => void; 'complete' is defined in this context. What exactly does this signify?

Currently, I am following a tutorial and after meticulously checking and rechecking, I can confidently say that my code matches exactly with the one the professor is using. The tutorial involves creating a simple Single Page Application in Angular using Ty ...

Is it possible for a function within a nodejs module to be defined but display as undefined upon access?

I am currently developing a Discord bot using NodeJS and TypeScript, and I'm facing an issue while trying to import custom modules in a loop with the following code: const eventFiles = fs.readdirSync("./src/events/").filter((file: string) =& ...

Having difficulty generating the appropriate output for ng-new app

I am facing difficulty in generating the Angular app after running the 'ng new my-app' command. Here are the outputs I receive upon running the command - 1 Executing 'ng new-mean' app https://i.sstatic.net/UaTQe.png 2 After choosing ...

Utilizing a directive for dynamically applying classes to the host element

Exploring the world of Angular 2 has been quite interesting. I recently grasped the concept of using the Angular Renderer to manipulate ElementStyle, and now I am eager to dive into the Renderer method: setElementClass(renderElement: any, className: strin ...

Navigating through Angular2 Tutorial (Tour of Heroes): Could not locate module with the name './app-routing.module'

Currently, I am working my way through the NG2 tutorial located here, however, I have come across a stumbling block. Upon attempting to import my AppRoutingModule in my app.module.ts file, I encounter a 'Cannot find module './app-routing.module& ...

Error: The function child.send is not defined as a valid function

Encountering an issue while attempting to build my Angular application. npm run build All tasks run smoothly on local machine and the project is successfully built. However, when trying to execute the command on the server via console (ssh), I am faced w ...

Visualizing hierarchical data in Angular 12 using D3 tree with clickable navigation links

I'm facing a challenge in displaying a routerLink within my d3.tree(). I've attempted to do so like this: .append("a") .html(`<a [routerLink]="/mycomponent" fragment="1.1">link to user component</a>`); However, the following code wor ...

The issue at hand is that the Mongo Atlas model is in place, but for some reason,

https://i.sstatic.net/4m2KT.pngI recently delved into using Next.js and I am a newcomer to backend technologies. I have successfully established a connection with MongoDB Atlas using Mongoose, however, the user object data from the post request is not be ...

Tips for incorporating Angular2 into Eclipse using TypeScript

Recently, I delved into the world of Angular 2 and noticed that TypeScript is highly recommended over JavaScript. Intrigued by this recommendation, I decided to make the switch as well. I came across a helpful guide for setting up everything in Eclipse - f ...

What is the method for defining an interface as the data type for a state?

My React functional component below fetches data from an API and stores it in the drinks state. The fetched data consists of an array of objects. Each object contains a key strCategory with a string value. To create a TypeScript interface for this data st ...

Error: The object does not have the ability to support the 'renderRows' property or method

My package.json settings are all set up correctly and 'ng serve' runs smoothly. However, when I try to run my application, the mat-table fails to load and the console throws an error message saying "TypeError: Object doesn't support property ...

Angular code can sometimes trigger TS2345 and TS2339 errors for seemingly straightforward tasks

I am currently in the process of learning how to utilize D3 with angular. As a beginner, I decided to work on a simple project that was originally created by William Liu a few years back (http://bl.ocks.org/WilliamQLiu/76ae20060e19bf42d774). While I manage ...

Angular/RXJS - Synchronize with AJAX response before proceeding

export class DropDownService { private dockerURL; constructor(private infoService: InfoService){ this.infoService.getVersion().subscribe((URL) => { this.dockerURL = URL; }); // Ensuring the ...

Emphasize the chosen row in Primeng table without needing to select the checkbox

Currently, I am utilizing the primeng library to implement a p-table combined with a p-checkbox. My goal is to enable the highlighting of a clicked row (or its content) without automatically checking the checkbox. The intention is for the Checkbox to only ...

Type inference error in TypeScript occurs within a conditional statement when the condition relies on the output of a function call rather than a boolean expression

In my TypeScript code, I have a Linked List class that is working perfectly. The class includes a Node type and functions to add items to the list. type ListItem = number | string | object; class Node { private value: ListItem; private next: Node | nu ...

The WebSocket connection attempt to 'ws://localhost:5000/notificationHub' was unsuccessful due to an error encountered during the WebSocket handshake, resulting in an unexpected response code of 307

I have successfully integrated SignalR on both my Angular client and ASP.NET Core WebAPI. However, I am encountering an error when the client attempts to connect to the server: WebSocket connection to 'ws://localhost:5000/notificationHub' failed: ...

What is the method for importing the merge function from "lodash/merge" in TypeScript?

When using ES6, it's possible to import only a single function from a library in order to reduce the overall bundle size. For example: import merge from "lodash/merge" But in TypeScript, the statement above can trigger a compile error: Cannot find m ...

Guide on establishing a connection between two MongoDB collections, utilizing TypeScript

'm currently working on establishing a connection between the user and contestant collections. My goal is to retrieve contestants that have been created by a specific user, identified by their user id. And I'm implementing this in typescript. us ...