Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How can I pass a function as a parameter in an angular HTML template to update a variable in the parent component?

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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 class="current-page">Current Page: {{ page }}</p>
<section>
    <div class="button-row">
        <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 class="numbered-buttons" *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.

>Solution :

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;
        });
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading