Home > Mobile >  How can I pass a function as a parameter in an angular HTML template to update a variable in the par
How can I pass a function as a parameter in an angular HTML template to update a variable in the par

Time:12-11

Hello fellow programmers. Just for context, this is sort of like an IMDb-inspired web application that I'm trying to build for the first time in Angular.

I am currently trying to make my pagination component into a reusable one as I was previously mostly just copying and pasting code into other components that required pagination.

So for my main MoviesComponent, it will show movies in a grid-like view and at the bottom, we will have pagination for these movies:

movies.component.html

...

<app-pagination
    [maxPage]="maxPage"
    [page]="page"
    [updateItemsList]="fetchMovieList"
>

movies.component.ts

import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { WebService } from '../web.service';
import { map, startWith } from 'rxjs/operators';
import { MatDialog } from '@angular/material/dialog';
import { FilterDialogComponent } from './filter-dialog/filter-dialog.component';
import { Movie } from '../interfaces/movie';

@Component({
    selector: 'app-movies',
    templateUrl: './movies.component.html',
    styleUrls: ['./movies.component.css']
})
export class MoviesComponent implements OnInit {
    movies_list: any;
    page: number = 1;
    maxPage: number = 0;;

    constructor(
        private webService: WebService,
        public filterDialog: MatDialog
    ) { }

    ngOnInit(){
        if (sessionStorage['page']) {
            this.page = Number(sessionStorage['page']);
        }

        this.fetchMovieList();

        this.webService.getMaxPage().subscribe((data: any) => {
            this.maxPage = data['max_page'];
        });
        
    }

    fetchMovieList(): void{
        this.webService
            .getMovies(this.page)
            .subscribe((data: any) => {
                this.movies_list = data;
            });
    }

I am trying to update the movies_list list (shown above) whenever a user clicks on one of the next, previous, last, first buttons to update my pagination, which I am trying to achieve by passing a function into my PaginationComponent class like so:

pagination.component.ts

import { Component, Input, OnInit } from '@angular/core';
import { WebService } from '../web.service';

@Component({
    selector: 'app-pagination',
    templateUrl: './pagination.component.html',
    styleUrls: ['./pagination.component.css']
})
export class PaginationComponent implements OnInit {
    @Input() maxPage: number = 0;
    @Input() page: number = 1;
    @Input() updateItemsList!: () => void;

    constructor(
    ) { }

    ngOnInit(): void {
    }

    updatePagination(): any {
        sessionStorage['page'] = this.page;
        this.updateItemsList()
    }

    getPageArray() {
        if (this.maxPage > 5) {
            if (this.page > 3 && this.page <= (this.maxPage - 2)) {
                return [...Array(5).keys()].map(x => x   (this.page - 2));
            } else if (this.page > (this.maxPage - 2)) {
                return [...Array(5).keys()].map(x => x   (this.maxPage - 4));
            } else {
                return [...Array(5).keys()].map(x => x   1);
            }
        } else {
            return [...Array(this.maxPage).keys()].map(x => x   1);
        }
    }

    firstPage() {
        this.page = 1;
        this.updatePagination()
    }

    previousPage() {
        if (this.page > 1) {
            this.page--;
            this.updatePagination()
        }
    }

    goToPage(page: number) {
        this.page = page;
        this.updatePagination()
    }

    nextPage() {
        if (this.page < this.maxPage) {
            this.page  ;
            this.updatePagination()
        }
    }

    lastPage() {
        this.page = this.maxPage;
        this.updatePagination()
    }
}

pagination.component.html

<p >Current Page: {{ page }}</p>
<section>
    <div >
        <button 
            mat-stroked-button 
            matToolTipcolor="primary" 
            matTooltip="Go to first page"
            (click)="firstPage()">
            <<
        </button>
        <button 
            mat-stroked-button 
            color="primary"
            matTooltip="Go to previous page"
            (click)="previousPage()">
            <
        </button>
        <div  *ngFor="let i of getPageArray()">
            <button mat-raised-button color="primary" (click)="goToPage(i)">{{i}}</button>
        </div>
        <button 
            mat-stroked-button 
            color="primary" 
            matTooltip="Go to next page"
            (click)="nextPage()">
            >
        </button>
        <button 
            mat-stroked-button 
            color="primary" 
            matTooltip="Go to last page"
            (click)="lastPage()">
            >>
        </button>
    </div>
</section>

Unfortunately, whenever I click on one of these buttons. I get a ERROR TypeError: Cannot read properties of undefined (reading 'getMovies')

I am nearly sure it's something to do with the way I am calling the function in the wrong scope as if I add webService: WebService in the constructor params in PaginationComponent the error goes away but nothing quite happens when I click the buttons. I come from a React FP background so I might be getting some OOP concepts or angular data-binding wrong.

Any help would be appreciated :)

P.S. Let me know how I did on my first StackOverflow post. I hope I explained everything clearly and don't want to annoy anyone on a Friday afternoon. Thanks guys for your help! I look forward to hearing your feedback.

CodePudding user response:

use an output from the child to trigger a function in the parent, don't pass a function to a child for it to call.

change the following in pagination component:

@Output() updateItemsList = new EventEmitter<number>();

updatePagination(): any {
    sessionStorage['page'] = this.page;
    this.updateItemsList.emit(this.page);
}

in parent template, bind to it:

<app-pagination
    [maxPage]="maxPage"
    [page]="page"
    (updateItemsList)="fetchMovieList($event)"
>

in parent:

ngOnInit(){
    ...

    this.fetchMovieList(this.page);

    ...
}

fetchMovieList(page: number): void{
    this.webService
        .getMovies(page)
        .subscribe((data: any) => {
            this.movies_list = data;
        });
}
  • Related