1

I've been trying to make this work for longer than I'd care to admit. Lots of examples on Stackoverflow that are close to what I'm trying to do, but none that seem to answer my question exactly, so I'm asking my own question. I'm fairly new to Angular development. Here's what I'm trying to do:

I'm trying to display data in the fancy bootstrap 4 table that has pagination, column sorting and text search. I want the data that is displayed in the table to be dependent upon what is selected in a drop-down (ultimately it will be dependent upon 2 drop-downs, but I am simplifying the example for the purposes of this question). That data that populates both the select list and table should come from a service.

I built a Stackblitz to demonstrate what I'm trying to do, but don't know how to wire it up the rest of the way. Any help would be greatly appreciated!

1 Answer 1

1

For this to take place:

  • Every time a person is selected/changed in the person component, you need to pass this to the table component
  • Filter the countries based on the person selected and passed [not enough information to do this for you here]
  • using your existing stackblitz, make the following changes to implement the first bullet above

after changes, the person.component.html:

<form>
  <div class="form-group form-inline">
    <select name="person" id="person" (change)='someFunc($event)' [(ngModel)]="selectedOption" >
      <option *ngFor="let user of users" [value]="user.userid" class ="blueText"

      >{{ user.name }}</option>
    </select>
    selected user: {{selectedUser}}
  </div>
</form>

after changes, the person.component.ts:

import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { PersonService } from './person.service';
import { User } from './user.ts';
import { ChangeAssigneeService } from './change-assignee.service';

@Component({
  selector: 'person',
  templateUrl: './person.component.html',
  styleUrls: ['./person.component.scss']
})
export class PersonComponent implements OnInit {

  users: User[];
  selectedUser:any;
 @Output() private personSelected = new EventEmitter<number>();


  constructor(private personService: PersonService, private changeassigneeService: ChangeAssigneeService) { 
    this.selectedUser = '';
  }

  ngOnInit() {
    this.users = this.personService.getAssignees();
  }

someFunc(event){
  this.personSelected.emit(event.target.value);
  this.selectedUser = event.target.value;
}

  changeAssignee(val:any){
    this.changeassigneeService.sendAssignee(val);
    console.log("test");
  }

}

after changes, the table-complete.html:

<form>
  <person (personSelected)="onpersonSelected($event)"></person>
  we got <mark>{{personFromChild || 'nothing'}}</mark> from the table component
  <div class="form-group form-inline">
      Full text search: <input class="form-control ml-2" type="text" name="searchTerm" [(ngModel)]="service.searchTerm"/>
      <span class="ml-3" *ngIf="service.loading$ | async">Loading...</span>
  </div>

  <table class="table table-striped">
    <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col" sortable="name" (sort)="onSort($event)">Country</th>
      <th scope="col" sortable="area" (sort)="onSort($event)">Area</th>
      <th scope="col" sortable="population" (sort)="onSort($event)">Population</th>
    </tr>
    </thead>
    <tbody>
    <tr *ngFor="let country of countries$ | async">
      <th scope="row">{{ country.id }}</th>
      <td>
        <img [src]="'https://upload.wikimedia.org/wikipedia/commons/' + country.flag" class="mr-2" style="width: 20px">
        <ngb-highlight [result]="country.name" [term]="service.searchTerm"></ngb-highlight>
      </td>
      <td><ngb-highlight [result]="country.area | number" [term]="service.searchTerm"></ngb-highlight></td>
      <td><ngb-highlight [result]="country.population | number" [term]="service.searchTerm"></ngb-highlight></td>
    </tr>
    </tbody>
  </table>

  <div class="d-flex justify-content-between p-2">
    <ngb-pagination
      [collectionSize]="total$ | async" [(page)]="service.page" [pageSize]="service.pageSize">
    </ngb-pagination>

    <select class="custom-select" style="width: auto" name="pageSize" [(ngModel)]="service.pageSize">
      <option [ngValue]="2">2 items per page</option>
      <option [ngValue]="4">4 items per page</option>
      <option [ngValue]="6">6 items per page</option>
    </select>
  </div>

</form>

after changes, the table-complete.ts:

import {DecimalPipe} from '@angular/common';
import {Component, QueryList, ViewChildren, OnInit} from '@angular/core';
import {Observable} from 'rxjs';
import { User } from './user.ts';

import {Country} from './country';
import {CountryService} from './country.service';
import {NgbdSortableHeader, SortEvent} from './sortable.directive';
import { ChangeAssigneeService } from './change-assignee.service';


@Component(
    {selector: 'ngbd-table-complete', 
    templateUrl: './table-complete.html', 
    providers: [CountryService, DecimalPipe]})
export class NgbdTableComplete implements OnInit {
  countries$: Observable<Country[]>;
  total$: Observable<number>;
  personFromChild:any;

  //userId$: Observable<string>;
  userId$: string;

  @ViewChildren(NgbdSortableHeader) headers: QueryList<NgbdSortableHeader>;

  constructor(public service: CountryService, private changeassigneeService: ChangeAssigneeService) {
    this.countries$ = service.countries$;
    this.total$ = service.total$;
  }

  public onpersonSelected(personPassed: number) {
    this.personFromChild = personPassed;
    /* INSERT the logic to select countries based on this selected value */
  }


  ngOnInit(){
    this.changeassigneeService.changeAssignee$.subscribe(message => {
      this.userId$ = message;
      console.log(message);
    });
  }

  onSort({column, direction}: SortEvent) {
    // resetting other headers
    this.headers.forEach(header => {
      if (header.sortable !== column) {
        header.direction = '';
      }
    });

    this.service.sortColumn = column;
    this.service.sortDirection = direction;
  }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the reply @Akber! I really appreciate it. I forked my stackblitz and made your changes. link This is close to working, but not quite there. I have a few other questions. Take a look at table-complete.ts. The table loads from an Observable array of type Country[]. I started modifying the onpersonSelected function that you built, but Angular is angry because I'm returning an array of Type Country instead of an Observable.
In the real world, I'd pass the user id into a data service and run a query in a sql database to return the applicable data for the countries. How do I simulate that? Thanks again!
There are only a finite number of countries, so the data is tiny; I would load all countries (from service) into array up-front, as soon as the UserID changes, i would search the array and return the result immediately; If data is huge, i would create a REST API that return only relevant countries only for the UserID that is passed, instead of loading all countries (big transaction over the network) and then filtering it in my service or component, I would have to add a "loading gif" & user experience would be dependent on the network lag (a dependency i'd like to avoid for mobile/3G users)
thanks for the quick reply. How would I search the array and return the results immediately? I can't seem to make it work in my example. i appreciate the help.
the countries array as it is has no link to the userIDs... the data available to filter should have a condition that selects countries based on a userID - the current country array has none of the fields (amongst id, name, flag, area, population) which we can use to filter
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.