1

I want to use ngx-virtual-scroller for sections of items.

<virtual-scroller #scroll [items]="???">
    <div *ngFor="let group of groups;">    
       <div class="title">
            <span>{{group.name}}</span>
       </div>

       <my-item *ngFor="let item of group.items">
       </my-item>
     </div>
</virtual-scroller>

What should I put in the items of the virtual scroll of ngx-virtual-scroller?

I need to have a title for a group of items.. each item belong to a group.

i.e. the title should be in a line of itself and the item height is bigger then the title's height.

Edited:

I forgot to mention that the number of groups is dynamic and also the number of items in each group is dynamic. Also, the number of items in a group could be a very big number, so I don't want that the items of the scroll will be the groups because if the group is very big, I don't want to load all of it to the DOM.

RoG
  • 167
  • 2
  • 15

1 Answers1

1

I know your question is about ngx-virtual-scroller, but I have a solution for ngx-ui-scroll, which might be helpful for you or someone who deals with dataset grouping. Big part of the logic seems independent of the scroller implementation/api, but let me think I'm doing it for ngx-ui-scroll.

In this case we need to treat groups as the datasource which should implement get method in accordance with ngx-ui-scroll docs. Like following

groups = new Datasource({
  get: (index, count, success) =>
    success(this.getItems(index, count)
  )
})

I don't know the structure of your data, and this should not be important because we are discussing the approach. The only thing we need to take into account is a consistency between the Datasource and the template. Imagine we already have flat items dataset of fixed length

data = [];
MIN = 1;
MAX = 200;

constructor() {
  for (let i = this.MIN; i <= this.MAX; i++) {
    this.data.push({ id: i, text: 'item #' + i });
  }
}

And you want to group them. How may groups you'll have? It depends on how many items you want to be in a single group.

this.ITEMS_PER_GROUP = 3;
this.groupsCount = Math.ceil((this.MAX - this.MIN) / this.ITEMS_PER_GROUP);

Then going back to getItems implementation, I'd suggest to split items retrieving procedure into 2 methods. Getting groups (by index-count due to ngx-ui-scroll api):

getItems(index: number, count: number) {
  const data = [];
  const start = Math.max(1, index);
  const end = Math.min(this.groupsCount, index + count - 1);
  if (start <= end) {
    for (let i = start; i <= end; i++) {
      const group = this.getGroup(i);
      if (group) {
        data.push(group);
      }
    }
  }
  return data;
}

and getting one group by index:

getGroup(index: number) {
  let result = null;
  const data = [];
  const start = this.MIN + (index - 1) * this.ITEMS_PER_GROUP;
  const end = start + this.ITEMS_PER_GROUP - 1;
  if (start <= end) {
    for (let i = start; i <= end; i++) {
      const item = this.data.find(item => item.id === i);
      if (item) {
        data.push(item);
      }
    }
    result = {
      title: 'Group ' + index,
      items: data
    };
  }
  return result;
}

This way you'll get the datasource consistent with what you need in the template layer. With ngx-ui-scroll it will look as following:

<div *uiScroll="let group of groups">
  <div>{{group.title}}</div>
  <div *ngFor="let item of group.items">
    <div class="item">{{item.text}}</div>
  </div>
</div>

Speaking of ngx-ui-scroll, all limitations could be removed, I mean you may not know the boundaries of the dataset and groups count, but the implementation would be slightly different.

At last, I created demo, as the case seems useful for ngx-ui-scroll users

https://stackblitz.com/edit/angular-ngx-ui-scroll-1-3-4-grouping-datasource

dhilt
  • 13,532
  • 6
  • 48
  • 67
  • Hi, thanks for you answer. I forgot to mention that I am getting all the groups from other service. The number of groups is dynamic and the number of items in each group is dynamic and it's could be a very big number of items in a group. This is why I didn't wanted to do items=groups and because if I have a big group, I don't want to load all of it to the DOM. – RoG Feb 12 '20 at 09:37
  • @RoG In this case I would try an opposite, say, flatterning approach: make an additional layer dedicated to provide an array of items to the scroller built from a group array in accordance with app/scroller cumulative requirements, restricted by flat item list index, not by group index. Group title may be treated as another item in the result flat items flow. – dhilt Feb 12 '20 at 13:39
  • Yes, this is what I though to do, but I wanted to know if there is any other solution that I don't think of it. The problem in this solution is that the title should be in a line of itself, but the items could be 5 in a row or more/less depend of the width of the screen... this should be multi column scroll and also unequal children sizes (the title size is different from item size). – RoG Feb 13 '20 at 10:04
  • 1
    @RoG The requirements are too specific, this seems possible, but I'm afraid none of the ready-made solutions would allow you to do it out of box, so you'll have to implement it by yourself – dhilt Feb 13 '20 at 10:46