0

I am trying to create a select dropdown where a user selects multiple days of the week and it filters the table to show only the rows that are selected for those days of the week. The filter is called Delivery Days. The data in the table row shows as 'Mo' for Monday, 'Tu' for Tuesday, etc. because that is how it will show in the database.

The delivery days will only be displayed for the expanded/children rows not the main row. So when filtering by delivery days it will only filter for the expanded rows because those are the rows that will show delivery days data.

This is the code that I added for the Delivery Days filter in HTML and TypeScript.

I commented out the html for the select for days of the week because I'm getting a

Template parse errors: Unexpected character "EOF"'.

I'm not sure why I'm getting this error.

I updated the dependencies from Angular 8 to 20 and it is taking a long time to compile.

HTML

 <mat-select [formControl]="days" multiple>
   <mat-select-trigger>      
   {{days.value?.[0] || ''}}
   @if ((days.value?.length || 0) > 1) {
     <span class="example-additional-selection">
      (+{{(days.value?.length || 0) - 1}} {{days.value?.length === 2 ? 'other' : 'others'}})
     </span>
   }    
   </mat-select-trigger>
   @for (topping of daysList; track day) {
    <mat-option [value]="day">{{day}}</mat-option>
   }
</mat-select>

TypeScript

days = new FormControl('');

  dayList: string[] = [
   'Monday',
   'Tuesday',
   'Wednesday',
   'Thursday',
   'Friday',
   'Saturday',
   'Sunday',
  ];

Stackblitz demo

I'm also unable to see the Delivery Days drop down to click off of the options to go back to the dropdown after making selections.

Delivery Days unable to see

10
  • 1
    you have "angular 8" and you're using @for{..} and @if Commented Sep 3 at 17:00
  • @Eliseo updated to angular 20 Commented Sep 3 at 17:30
  • @Eliseo after updating dependencies it takes a while to compile. It's still compiling now. Any suggestions? Commented Sep 3 at 17:54
  • 1
    BTW, enclose the label and mat-select in a mat-form-field and be carefull, you write daysList and is dayList (without "s" Commented Sep 3 at 18:35
  • 1
    You can use disableOptionCentering and panelClass, e.g. <mat-select [formControl]="days" multiple disableOptionCentering panelClass="days-offset">, and define in styles.css {.days-offset{margin-top:2rem;} Commented Sep 4 at 17:25

1 Answer 1

1

Update the mat-tigger should be like

    <mat-select-trigger>
      {{days.value && days.value.length?days.value[0].text:''}}
      ...
    </mat-select-trigger>

Yet corrected in the answer

If we want to see the days selected, we can use a new variable

  daysSelected:string="all";
  //and is days.valuesChange
    this.sub=this.days.valueChanges.subscribe((res:any[])=>{
        this.dataSource.filter = res.map((x:any)=>x.value).join(',');
        //add this line
        this.daysSelected = res.map((x:any)=>x.text).join(',')|| "all";
    })

To filter a dataSource we need take account two properties: filter (that is a string) and filterPredicate that is a function with two arguments, the object of the row, and a string, and return true or false.

If we not define the "filterPredicate" the function return true if any property of the object contains the string, but we can define e.g.

filter(data:T,filter:string)
{
   return data.symbol==filter;  //only if the "symbol" contains the filter
}

Remember that the function filter only execute if filter has value, so we needn't check in the function "filter"

But the first is use a more confortable dayList. If you define

  dayList: any[] = [
    {value:'Mo',text:'Monday'},
    {value:'Tu',text:'Tuesday'},
    {value:'We',text:'Wednesday'},
    {value:'Th',text:'Thursday'},
    {value:'Fr',text:'Friday'},
    {value:'Sa',text:'Saturday'},
    {value:'Su',text:'Sunday'}
  ];

Your mat-select like, see that the "value" of day is an array of object

<mat-form-field>
  <mat-label>Delivery Day</mat-label>
  <mat-select [formControl]="days" multiple>
    <mat-select-trigger>
      <!--see you show days.value[0].text-->
      {{days.value && days.value.length?days.value[0].text:''}}
      <span
        *ngIf="((days.value?.length || 0) > 1)"
        class="example-additional-selection"
      >
        (+{{(days.value?.length || 0) - 1}} {{days.value?.length === 2 ? 'other'
        : 'others'}})
      </span>
    </mat-select-trigger>
    <!--see that the value is the whole "day"-->
    <!--e.g. days.valus will be the array [{value:'Mo',text:'Monday'},
                                           {value:'Sa',text:'Saturday']
        if you select Monday and Saturday-->
    <mat-option *ngFor="let day of dayList" [value]="day"
      >{{day.text}}</mat-option
    >
  </mat-select>
</mat-form-field>

in a subscription to day.valueChange you give value to this.dataSource.filter (don't forget unsubscribe)

See that dataSource.filter is a String.


  sub:Subscription;

  ngAfterViewInit() {
    this.dataSource.sort = this.sort;
    this.sub=this.days.valueChanges.subscribe((res:any[])=>{
      this.dataSource.filter = res.map((x:any)=>x.value).join(',');

    })
  }
  //to unsubscribe
  ngOnDestroy()
  {
    this.sub && this.sub.unsubscribe()
  }

Well the function filter

filter(data:any,filter:string)
{
   //data will be, e.g. {position: 10,Name: 'Neon',...DeliveryDay: ['Su', 'Mo']}
   //filter wil be, e.g. Su,Tu

   const days=filter.split(',') //e.g. days=['Su','Tu']
   for (let i=0;i<days.length;i++)
   {
      if (data.DeliveryDay.includes(days[i])
         return true;
   }  
   return false;
}

Well, really we can make more "compact" the function filter

filter(data:any,filter:string)
{
   const days=filter.split(',') //e.g. days=['Su','Tu']
   let result=false;
   days.forEach(x=>result=result||data.DeliveryDay.includes(x))
   return result;
}

or even more compact using reduce

filter(data:any,filter:string)
{
   const days=filter.split(',') //e.g. days=['Su','Tu']
   return days.reduce((a:boolean,b:string)=>a||data.DeliveryDay.includes(b),false)
}

The last piece of the jigsaw is indicate that our dataSource use this filter function

  ngAfterViewInit() {
    this.dataSource.sort = this.sort;
    this.dataSource.filterPredicate=this.filter; //<--this line
    this.days.valueChanges.subscribe((res:any[])=>{
      this.dataSource.filter = res.map((x:any)=>x.value).join(',');

    })
  }

Your forked stackblitz in Angular 8

Sign up to request clarification or add additional context in comments.

Comments

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.