Expand/Collapse ngFor data upon clicking a button

I am facing an issue with toggling the display of ngFor data. I want to initially show one record and then reveal the rest when the "See more" button is clicked. However, I do not want the previously shown records to be automatically hidden. Currently, opening a new itinerary automatically closes the previous one. Can anyone help me out with this? Below is the components file.

<div class="itineraryDay" *ngFor="let eventList of eventDailyItineraray; let i = index">
<div class="itineraryDay-header">
    <h5>Day {{i+1}}</h5>
    <p>{{eventList.tourevents[0]?.categoryName}}</p>
</div>
<div class="itineraryDay-detail">
    <div class="itinerary-cards">
        <mat-card class="mycard" *ngFor="let event of eventList.tourevents | slice:0:[selectedIndex === i ? max  : 1 ]; let j = index">
            <div class="day-activity d-flex">
                <div class="activity-img">
                    <img [src]="(event?.eventPic ? firstEventImage(event?.itemId,event?.eventPic):'./assets/media/icons/preview-item.svg')" />
                </div>
                <div class="activity-detail">
                    <div class="activity-detail-inner d-flex justify-content-between align-items-center">
                        <div class="activity-category d-flex">
                            <img src="{{categoryPic}}{{event?.categoryIcon}}" />
                            <span>{{event?.itemName}}</span>
                        </div>
                        <div class="activity-hours d-flex align-items-center justify-content-end">
                            <img src="./assets/media/icons/access_time-24px.svg" />
                            <span>{{durationCalculation(event?.duration)}} Hours</span>
                        </div>
                    </div>
                    <p>{{event?.description}}</p>
                </div>
            </div>
        </mat-card>
    </div>
    <div class="see-more">
        <div class="icon-inner">
            <p (click)="toggle(i, $event)" class="seeMore">See More</p>
            <div class="more-icon" (click)="toggle(i, $event)" [ngClass]="{'active': selectedIndex === i}" >
                <i class="flaticon-eye"></i>
            </div>
        </div>
    </div>
</div>

Here is the click function in ts file.

toggle(index, event){

this.max = this.eventDailyItineraray[index].tourevents.length;

if(this.selectedIndex === index){
  this.selectedIndex = -1;
  event.currentTarget.parentElement.children[0].innerText = 'See More';
}else{
  this.selectedIndex = index;
  event.currentTarget.parentElement.children[0].innerText = 'See Less';
} } 

https://i.sstatic.net/TwsRP.png

Answer №1

If you need to manage a list of items, it's important to use an array instead of just a single variable. Define an array like this:

more: boolean[] = []

Additionally, when changing the text in the HTML, avoid using something as unusual as

event.currentTarget.parentElement.children[0].innerText = 'See More'
. Instead, consider using the "angular way" like this:

<p (click)="more[i] == !more[i]" class="seeMore">
    {{more[i] ? 'See Less' : 'See more'}}
</p>

Lastly, replace your

`selectedIndex === i` with `more[i]`

Answer №2

If you want to ensure smooth updates when dealing with changes, consider implementing the 'trackby' concept in your code. This will help track previous changes and seamlessly incorporate any new ones.

<...*ngFor="let item of items; trackBy:trackByIdentity">

Make sure to add the following method inside your .ts file:

trackByIdentity(index, item){
     return item.<unique_attribute_here>; 
}

Answer №3

In situations like these (even though it may seem like a simple solution, I haven't been able to think of anything better), what I typically do is include a specific value in each object within your iterable that indicates whether that item should have an active class or not. In this scenario, as you retrieve youeventList.tourevents, you can map through it and add something like (isActive = false) to each object, then link that value to the class of each item in your ngFor loop.

Therefore, when you click on the toggle button, simply pass the index of the ngFor item and update that value in your array - this will either add or remove the active class name for the selected item, without affecting the other items in your ngFor loop. Hopefully this explanation is clear; if necessary, I can provide example code in my response.

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

How can I showcase the captured image on Ionic 2?

I am having trouble displaying the selected or captured image on the page after uploading it through two methods - one using the gallery and the other using the camera. ** Here is my code ** 1) profile.html: <img class="profile-picture" src="{{baseUr ...

Deploying an angular 5 application

After deploying my Angular 5 app to hosting with the command ng build --prod --build-optimizer, I noticed that the routing links do not work properly on the server (only the main page works). The other components are not functioning as expected. Below is ...

What is the reason behind the NgForOf directive in Angular not supporting union types?

Within my component, I have defined a property array as follows: array: number[] | string[] = ['1', '2']; In the template, I am using ngFor to iterate over the elements of this array: <div *ngFor="let element of array"> ...

Completing a submission form in a separate module

I've developed a model-based form within a component, but the submit button is located in a different component. When the button is pressed, the following function is activated: @Input() form: any; ... if(this.form.valid) this.saveD ...

Angular Service Worker enhancements across various domains

Scenario Our team is currently developing an Angular application that is accessible through multiple domains. Depending on the domain, the app will display different colors and content, but it is essentially the same Angular application. To enhance perfo ...

Why aren't functions included when casting from a JSON literal to a specific type?

In my model class, the structure looks like this: export class Task { public name: string; public status: string = "todo"; public completeTask(): void { this.status = "done"; } } There is also a service responsible for retrie ...

In TypeScript, there is a mismatch between the function return type

I am new to TypeScript and trying to follow its recommendations, but I am having trouble understanding this particular issue. https://i.stack.imgur.com/fYQmQ.png After reading the definition of type EffectCallback, which is a function returning void, I t ...

What is the reason behind the narrowing of the type by indexing into a mapped type?

This question is inspired by an amazing answer found here: My curiosity lies in why the indexing works in the mapped type trick. Let's illustrate with an example: type MyData = { a: { alpha: string; }; b: { beta: number; } } type ...

Using TypeScript to define attributes by merging specified attribute names with variable attribute names

Can a TypeScript type/interface be created with the specified structure below? interface Model { id: number; something: string; somethingElse: Date; [key: string]: string | null; } It essentially consists of both defined attributes and 0 to n und ...

Steps to adding an item to a TypeScript array

My code involved creating an array called dataArray of type dataRows, which is an interface. dataArray: Array<dataRows>; In a function, I attempted to push a dataRows object into the array like this: this.dataArray.push(row) But for some ...

What is the best way to inform TypeScript that it is acceptable to reference an export that may or may not exist in the current revision of a module?

Using React's Profiler in Production is my aim, but the version of React I am currently using is React 16.5, which exports the unstable_Profiler. The team responsible for delivering an optimized asset bundle (including React) will be updating to Reac ...

angular2 ngFor is not functioning properly

I'm having an issue where I cannot get textboxes to appear whenever a user clicks a button. I am attempting to achieve this using ngFor, but for some reason, the ngFor is not iterating as expected. Even after trying to change the array reference with ...

Sequencing animations with *ngIf in Angular 6

In my component, there are two elements that utilize the same fade animation. Only one element is visible on screen at a time, controlled by *ngIf directives for visibility management. The animation is a simple fade in/fade out effect. When the state of m ...

The metadata for [object Module] does not contain any ngModule information

I recently made changes to my routes by switching them from lazy loaded using a string reference to lazy loading through a call to import. However, I am facing an issue where every time I try to navigate to one of the pages, I encounter the following erro ...

Finding a way to reference multiple components within a mapping function

In order to set a ref to each project within the map function, I am trying to pass forwardRef from child to parent. At the moment, I am only able to get a single Project. However, I need to set refs to an array list so I can work with it. Below is what I h ...

When using this.$refs in Vue, be mindful that the object may be undefined

After switching to TypeScript, I encountered errors in some of my code related to: Object is possibly 'undefined' The version of TypeScript being used is 3.2.1 Below is the problematic code snippet: this.$refs[`stud-copy-${index}`][0].innerHTM ...

Disable the default animation

Is there a way to disable the default animation of the Select label in my code snippet below? export default function TicketProfile(props: any) { return ( <Container> <FormControl sx={{ ml: 1, mr: 1, minWidth: 220 }}> <Inp ...

Unconventional way of assigning class properties in Typescript (Javascript): '?='

Recently, I came across the ?= assignment expression within a class property declaration. Can anyone provide some insight into what this means? I am familiar with the new Optional Chaining feature (object?.prop), but this particular syntax is unfamiliar t ...

How to Merge Items within an Array of Objects Using Typescript?

I'm currently facing a challenge in combining objects from an array of Objects in typescript. The structure of the array is as follows: 0: {type: 'FeatureCollection', features: Array(134)} 1: {type: 'FeatureCollection', features: ...

Is there a way to define one type parameter directly and another type parameter implicitly?

I am currently utilizing a UI-library that offers an API for constructing tables with a structure similar to this: type Column<Record> = { keys: string | Array<string>; render: (prop: any, record: Record) => React.ReactNode; } The l ...