1

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 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.

1 Answer 1

1

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;
        });
}
Sign up to request clarification or add additional context in comments.

1 Comment

Words cannot describe how thankful I am. That fixed everything! I am not quite sure I know how this works as I'm fairly new to angular but I will need to look up what @Output and the .emit functions means. Again, thank you!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.