Converting a button from non-favorite to favorite in Angular: Step-by-step guide

How can I change the state of a button that is inside * NgFor when consuming an API? The issue arises when I try to toggle the favorite status from one to another, as it changes all buttons instead of just the one clicked. Is there a way to maintain the state of each button individually?

Service

import { HttpClient } from "@angular/common/http";


@Injectable({
  providedIn: 'root'
})
export class ServiceService {

  constructor(private http: HttpClient) { }

  private url = 'https://rickandmortyapi.com/api/';

  getAllApi(){
    return this.http.get(`${this.url}/character`)
  }

  pagination(pagination:number){
    return this.http.get(`${this.url}/character/?page=${pagination}`)
  }

}

home.component.ts

import { Component, OnInit } from '@angular/core';
import { ServiceService } from 'src/app/services/service.service';


@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  Characters: any[] = []
  NewCharacters: any[] = []
  public page = 1;
  public status = false;

  constructor(private apiService: ServiceService) {
    this.getAllCharacters()
  }

  ngOnInit(): void {
  }

  getAllCharacters() {
    this.apiService.getAllApi()
      .subscribe((data: any) => {
        this.Characters = data.results;
        console.log(data)
      })
  }


  getNextCharacters(pagination: number) {

    this.page = this.page + 1;
    console.log(this.page)

    this.apiService.pagination(this.page)
      .subscribe((data: any) => {
        this.NewCharacters = data.results
        console.log(data)
      })
  }

  updateFavoriteStatus(status:boolean){
    this.status = status
  }

}

html

<div class="container mt-5">

    <button (click)="getNextCharacters(1)" class="btn btn-outline-dark m-2">Next</button>


    <div class="card-columns" *ngIf="page < 2; else elseBlock">
        <div class="card text-center" *ngFor="let character of Characters">
            <img class="card-img-top" [src]="character.image" alt="Card image cap">
            <div class="card-body">
                <h5 class="card-title">{{character.name}}</h5>
                <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional
                    content. This content is a little bit longer.</p>
            </div>
            <button (click)="updateFavoriteStatus(false)" *ngIf="status; else elseFav" class="btn btn-success btn-block"><i class="far fa-star"></i> Favorite</button>

            <ng-template #elseFav>
                <button (click)="updateFavoriteStatus(true)" class="btn btn-outline-success btn-block"><i class="fas fa-star"></i> Favorite</button>
            </ng-template>

        </div>
    </div>

    <ng-template #elseBlock>
        <div class="card-columns">
            <div class="card text-center" *ngFor="let newCharacter of NewCharacters">
                <img class="card-img-top" [src]="newCharacter.image" alt="Card image cap">
                <div class="card-body">
                    <h5 class="card-title">{{newCharacter.name}}</h5>
                    <p class="card-text">This is a longer card with supporting text below as a natural lead-in to
                        additional
                        content. This content is a little bit longer.</p>
                </div>
            </div>
        </div>
    </ng-template>



</div>

Appreciate any assistance provided.

Answer №1

The issue at hand is that the variable this.status is not specifically linked to any individual item; it is a state that affects the entire component. What you need is a status property that can be updated when the favorite button is clicked.

Check out this StackBlitz example

Answer №2

Instead of utilizing a class level status variable to store the favorite status for a user, incorporate a status key on each user object within the Personajes array.

The allApi function provided demonstrates how to include a status property on every user object.

private allApi() {
    this.apiService.getAllApi()
      .subscribe((data: any) => {
        this.Personajes = data.results.map ( user => {
           if (user && typeof user === 'object') {
               user['status'] = true;
           }
           return user;
        });
        console.log(data)
      })
  }

The statusFav function switches the user's status by setting it to true if it was false initially and vice versa.

public statusFav(user:any) {
    user.status = !user.status
}

You can also utilize the value of user.status in an ngClass directive to toggle between 'far' and 'fas' classes instead of duplicating the button code with an ngIf and ngElse condition.

<div class="card-columns" *ngIf="suma < 2; else elseBlock">
        <div class="card text-center" *ngFor="let personaje of Personajes">
            <img class="card-img-top" [src]="personaje.image" alt="Card image cap">
            <div class="card-body">
                <h5 class="card-title">{{personaje.name}}</h5>
                <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional
                    content. This content is a little bit longer.</p>
            </div>
            <button (click)="statusFav(user.status)" 
                    class="btn btn-success btn-block"> 
                   <i *ngIf="user.status" [ngClass]="{'fas': user.status, 'far': !user.status}" class="fa-star"></i> 
        </div>
    </div>
</div>

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

Angular is failing to detect a change in the initial element of an array

In my Angular app, I am working on displaying a list of dates for the current week. Users should be able to view previous weeks by clicking a button, so I am using an Observable to update the array of dates and trying to display the updated array. Althoug ...

A guide on toggling a div in Ionic3

Looking to improve user experience, I need to display a long text partially with an 'expand' button. Clicking the button will reveal the full text. User interface design: Default view: https://i.sstatic.net/whJDN.png After expansion: https:/ ...

What are the steps to adjust the size of a browser window in WebDriverJS?

Currently, I am utilizing WebDriverJS, the JavaScript bindings for WebDriver, to conduct basic frontend testing (powered by nodejs). Nevertheless, encountering challenges resizing the window and the documentation available appears somewhat unclear in my u ...

Tips for implementing Bootstrap dropdowns (ng2-bootstrap) within ag-grid-ng2

The following link leads to a Github issue that needs attention. Your help on this matter would be greatly valued. ...

API requests in React Native can be conveniently handled using a proxy

Is it possible to utilize a proxy for API calls in React Native? I have attempted setting the property "Proxy": "https://someapi.com" in package.json, but it seems to be ineffective. Can a proxy be used effectively in React Native? ...

What is the reason behind this infinite loop occurrence?

As a newcomer to the world of coding, I recently embarked on learning JavaScript. However, I have encountered an issue with a particular piece of code causing an infinite loop that has left me perplexed. Despite having a birthday(myAge) function within t ...

When trying to deploy to Heroku, node-gyp encounters an issue installing [email protected] and ultimately fails to rebuild

I am currently facing an issue with deploying a nodejs application on Heroku due to the node-gyp rebuild error associated with the base64 library. I have successfully run the application locally, but deployment on Heroku seems to be problematic. Any sugges ...

In need of an AJAX image viewer compatible with PHP or considering transforming an ASP image viewer to PHP

Currently, I am in search of a Javascript (preferably based on jQuery) Ajax image viewer with a zoom feature that is compatible with PHP. The closest match to what I require is a script called "thinDoc" which I found to be very impressive: thinDoc To se ...

Can someone help me create Three.js types using the frontend option I choose?

I'm currently developing a user-friendly browser application for editing shaders in three.js using react-three-fiber. I want to enhance the functionality by allowing users to add additional uniforms to the ShaderMaterial. However, I do not want to exp ...

"Exploring the differences between normalization structures and observable entities in ngrx

I'm currently grappling with the concept of "entity arrays" in my ngrx Store. Let's say I have a collection of PlanDTO retrieved from my api server. Based on the research I've done, it seems necessary to set up a kind of "table" to store th ...

Photo captured by camera is not stored in photo gallery

I am currently working on a basic image upload form that allows users to take photos using their phone camera and upload them. However, I have noticed that the pictures taken this way are not being saved to the gallery. Is there something missing in the H ...

Transforming data from a singular object into an array containing multiple objects with key-value pairs

Looking for assistance with converting data from a single object in JSON format to another format. Here is the initial data: var originalData = { "1": "alpha", "2": "beta", "3": "ceta" } The desired format is as follows: var convertedData = ...

What is the best way to modify an existing object in an Observable Array in Angular?

As I work on my Ionic 5 / Angular application, a challenge arises when attempting to update a Conversation object within the existing array of Conversation: private _conversations = new BehaviorSubject<Conversation[]>([ new Conversation( & ...

Troubleshooting: EADDRNOTAVAIL issue encountered on Heroku Node.js server

After successfully creating a nodejs server on OpenShift, I am now embarking on a new project and attempting to replicate the same server on Heroku. Below is a snippet of the code for my server: var http = require('http'); var port = process.en ...

Develop a precompiled library for Angular applications that utilizes Ahead-of-Time (AOT) compilation technology

My Angular 5 library is packaged for consumption by other apps in their node_modules. Currently, the app is compiled Just-in-Time(JIT) using rollup and gulp. I export it as a package, and developers use it in its JIT compiled form. After researching AOT ...

Guide to populating a dynamic dropdown menu by utilizing the formArray index when making a selection

I'm facing a situation where I need to add/remove form groups with multiple form controls. Let's consider this scenario: https://i.sstatic.net/N2HhL.png The first dropdown represents the country list, the second dropdown displays states based ...

What is preventing me from altering the array one element at a time?

I am working with an array and a class let questions = [ { questionText: '', answerOptions: [], }, ]; class Questions { constructor(questionText,answerOptions) { this.questionText = questionText; this.answerOptio ...

The function Quill.register is not valid and cannot be recognized

I am currently working on my Angular project that utilizes ngx-quill and I am looking to integrate the third-party module, quill-blot-formatter, into it. To resolve TypeScript linter errors, I have added @types/quill as a dev dependency and made sure to c ...

Exploring the concept of using a single route with multiple DTOs in NestJS

At the moment, I am utilizing NestJS for creating a restful API. However, I am currently facing an issue with the ValidationPipe. It seems to only be functioning properly within controller methods and not when used in service methods. My goal is to implem ...

Is it possible to traverse through a series of variable names in TypeScript to access a child object?

I am attempting to update the following code: this.selectedArray1.indexOf(someIndexObject); Here is the code I want to use as a replacement: var someVariable = "selectedArray1" this[someVariable].indexOf(someIndexObject); However, when I make the repla ...