简体   繁体   中英

remove item shopping cart angular

I would simply like to delete an item on click, I made a code but I have error, I've been stuck on it for 2 days.

ERROR TypeError: this.addedBook.indexOf is not a function

I have already asked the question on the site we closed it for lack of information yet I am clear and precise

Thank you for your help

service

export class BookService {

  url: string = 'http://henri-potier.xebia.fr/books';

  public booktype: BookType[];
  item: any = [];

  constructor(private http: HttpClient) { }

  getBookList(): Observable<BookType[]> {
    return this.http.get<BookType[]>(this.url);
  }

  addToBook() {
    this.item.push(this.booktype);
  }
}

addToBook() here for add book but i dont know how to use it to display added books in my ts file

ts.file

export class PaymentComponent implements OnInit {
    
      addedBook: any = [];
      product:any;
    
      constructor(private bookService: BookService) { }
    
      ngOnInit(): void {
        this.addedBook = this.bookService.getBookList();
      }
    
      delete() {
        this.addedBook.splice(this.addedBook.indexOf(this.product), 1);
      }
    }

html

<div class="product" *ngFor="let book of addedBook | async">
    <div class="product-image">
        <img [src]="book.cover" alt="book">
    </div>
    <div class="product-details">
        <div class="product-title">{{book.title}}</div>
    </div>
    <div class="product-price">{{book.price | currency: 'EUR'}}</div>
    <div class="product-quantity">
        <input type="number" value="1" min="1">
    </div>
    <div class="product-removal">
        <button class="remove-product" (click)="delete()">
            Supprimé
        </button>
</div>

interface

export interface BookType {
    title: string;
    price: number;
    cover: string;
    synopsis: string;
}

I think this.bookService.getBookList() returns Observable so for you case it is not the best solution use async pipe. You should simply subscribe to your server response and than asign it to your variable. and after deleting item only rerender your ngFor .

JS

 export class PaymentComponent implements OnInit {
        
          addedBook: any[] = [];
          product:any;
        
          constructor(private bookService: BookService) { }
    
          ngOnInit(): void {
          // Observable
            this.bookService.getBookList().subscribe(response =>{
              this.addedBook = response;
            });

          // Promise
          /*
          this.bookService.getBookList().then(response=>{
            this.addedBook = response;
          })*/
          }
        
          delete(){
          
   this.addedBook.splice(this.addedBook.indexOf(this.product), 1);
              // rerender your array
        this.addedBook = [...this.addedBook];
          }
        }

HTML

 <div class="product" *ngFor="let book of addedBook">
    <div class="product-image">
        <img [src]="book.cover" alt="book">
    </div>
    <div class="product-details">
        <div class="product-title">{{book.title}}</div>
    </div>
    <div class="product-price">{{book.price | currency: 'EUR'}}</div>
    <div class="product-quantity">
        <input type="number" value="1" min="1">
    </div>
    <div class="product-removal">
        <button class="remove-product" (click)="delete()">
            Supprimé
        </button>
</div>

I would suggest something like this

Service file

export class BookService {

  url: string = 'http://henri-potier.xebia.fr/books';

  //add an observable here
  private bookUpdated = new Subject<bookType>();

  public booktype: BookType[] = [];//initializa empty array

  item: any = [];

  constructor(private http: HttpClient) { }

  //Ive changet the get method like this
  getBookList(){
   this.http.get<bookType>(url).subscribe((response) =>{
     this.bookType.push(response);//Here you add the server response into the array
      //here you can console log to check eg: console.log(this.bookType);
      //next you need to use the spread operator
      this.bookUpdated.next([...this.bookType]);
    });
  }

  bookUpdateListener() {
    return this.bookUpdated.asObservable();//You can subscribe to this in you TS file
    }
}

Now in your TS file you should subscribe to the update listener. This is typically done in NgOnInit

Something like this:

export class PaymentComponent implements OnInit {
    
      addedBook: BookType;
      product:any;
    
      constructor(private bookService: BookService) { }
    
      ngOnInit(): void {
        this.bookService.bookUpdateListener().subscribe((response)=>{
         this.addedBook = response;//this will happen every time the service class 
 //updates the book
         });
       //Here you can call the get book method
      this.bookService.getBookList();

      }
    
      delete() {
        this.addedBook.splice(this.addedBook.indexOf(this.product), 1);
      }
    }

Essentially what happens is you are subscribed to when books get changed or updated. Now you can simply use addedBook.title or whatever you want in your HTML.

UPDATE

I built a special stackblitz so you can see it in action here is the link ;

you can't use javascript splice on Observable stream, it is not an Array .
to be able to remove an item from a stream you need to combine it (the stream) with another stream (in your case) the id of the item you want to remove.

so first create 2 streams

// the $ sign at the end of the variable name is just an indication that this variable is an observable stream

bookList$: Observable<any[]>; // holds bookList stream
deleteBook$ = new Subject<{ id: string }>(); // holds book id stream

now pass the results you get from your database (which is an observable stream) to bookList$ stream you just created like that

ngOnInit(): void {

    this.bookList$ = this.bookService.getBookList().pipe(
        delay(0)
    );

}

change your html template to that.. and pipe the results from database like that

<div class="product" *ngFor="let book of (bookList$ | sync)">
    
    ...
     
    // make sure you include your`remove-product` button inside `*ngFor` loop so you can pass the `book id` you want to remove to the `delete()` function.       
    <button class="remove-product" (click)="delete(book)">
        Supprimé
    </button>

</div>

now back to your ts file where we gonna remove the item from the STREAM by modifying the Array and return a new stream.

bookList$: Observable<any[]>; // holds bookList stream
deleteBook$ = new Subject<{ id: string }>(); // holds book id stream


ngOnInit(): void {

    this.bookList$ = this.this.bookService.getBookList().pipe(
        delay(0)
    );


    combineLatest([
        this.bookList$,
        this.deleteBook$
    ]).pipe(
        take1(),
        map(([bookList, deleteBook]) => {
            if (deleteBook) {
                var index = bookList.findIndex((book: any) => book.id === deleteBook.id);
                if (index >= 0) {
                    bookList.splice(index, 1);
                }
                return bookList;
            }
            else {
                return bookList.concat(deleteBook);
            }
        })
    ).subscribe();

}

now all is left to do is remove the item

delete(book: any) {

    this.deleteBook$.next({ id: book.id }); pass the book you want to remove to the stream, `combineLatest` will take care of the rest
}

if you make an exit please don't forget me :) good luck!

From your code, we can see that getBookList() return an Observable. As addedBook is not a array reference it will won't have array methods. That is the cause for your issue.

If you want to do some operations from the service data, subscribe to the observable and store the reference of the value to addedBook .

export class PaymentComponent implements OnInit {

  ...

  ngOnInit(): void {
    this.bookService.getBookList().subscribe(
        res => { this.addedBook = res }  
    );
  }

  ...
}

And you need to remove the async keyword from your html

Typescript is mainly used to identify these kind of issues in compile time. The reason it doesn't throw error on compile time is that you've specified addedBook as any. While declaring you declare it as array and onInit you change it to observable, which can be avoided if you've specified type[] ex: string[]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM