Lista suspensa virtual
O componente Ignite UI for Angular Drop Down pode ser totalmente integrado à diretiva IgxForOf
para exibir uma lista muito grande de itens para sua seleção.
Angular Virtual Drop Down Example
Usage
First Steps
Para configurar o drop-down para exibir uma lista de itens virtuais, você precisa cumprir alguns pré-requisitos. Primeiro, precisamos importar o IgxForOfModule
no módulo do componente que declarará nosso drop-down.
// app.module.ts
import { IgxForOfModule } from 'igniteui-angular';
// import { IgxForOfModule } from '@infragistics/igniteui-angular'; for licensed package
@NgModule({
imports: [
...
IgxForOfModule
]
})
export class AppModule {}
Template Configuration
Em seguida, precisamos criar o modelo do componente drop-down, fazendo um loop pelos dados usando *igxFor
em vez de *ngFor
. A diretiva *igxFor
precisa de alguma configuração adicional para exibir corretamente todos os itens:
<!-- drop-down-virtual.component.html -->
<button igxButton [igxToggleAction]="dropdown"
[igxDropDownItemNavigation]="dropdown">
Item Series
</button>
<igx-drop-down #dropdown>
<div class="drop-down-virtual-wrapper" style="height: {{ itemsMaxHeight }}px;">
<igx-drop-down-item
*igxFor="let item of items; index as index;
scrollOrientation: 'vertical';
containerSize: itemsMaxHeight;
itemSize: itemHeight;"
[value]="item" [isHeader]="item.header"
role="option" [disabled]="item.disabled"
[index]="index">
{{ item.name }}
</igx-drop-down-item>
</div>
</igx-drop-down>
<div>Selected Model: <span>{{ dropdown.selectedItem?.value.name }}</span></div>
Os parâmetros adicionais passados para a diretiva *igxFor
são:
index
- captura o índice do item atual no conjunto de dadosscrollOrientation
- deve ser sempre'vertical'
containerSize
- o tamanho do contêiner virtualizado (empx
). Isso precisa ser imposto no<div>
de encapsulamento tambémitemSize
- o tamanho dos itens que serão exibidos (empx
)
Para garantir a exclusividade dos itens, passe item
dentro da entrada value
e index
dentro da entrada index
do igx-drop-down-item
. Para preservar a seleção durante a rolagem, o item drop-down precisa ter uma referência aos itens de dados aos quais está vinculado.
Note
Para que o menu suspenso funcione com uma lista virtualizada de itens, as entradas value
e index
devem ser passadas para todos os itens.
Note
É altamente recomendável que cada item tenha um valor único passado para a entrada [value]
. Caso contrário, pode levar a resultados inesperados (seleção incorreta).
Note
Quando o menu suspenso usa itens virtualizados, o tipo de dropdown.selectedItem
se torna { value: any, index: number }
, onde value
é uma referência ao item de dados passado dentro da entrada [value]
e index
é o índice do item no conjunto de dados
Component Definition
Dentro do construtor do componente, declararemos uma lista moderadamente grande de itens (contendo cabeçalhos e itens desabilitados), que serão exibidos no menu suspenso. Também precisaremos declarar itemHeight
e itemsMaxHeight
:
// drop-drop-virtual.component.ts
export class DropDownVirtualComponent {
public items: DataItem[];
public itemHeight = 48;
public itemsMaxHeight = 320;
constructor() {
const itemsCollection: DataItem[] = [];
for (let i = 0; i < 50; i++) {
const series = (i * 10).toString();
itemsCollection.push({
id: series,
name: `${series} Series`,
header: true,
disabled: false
});
for (let j = 0; j < 10; j++) {
itemsCollection.push({
id: `${series}_${j}`,
name: `Series ${series}, ${i * 10 + j} Model`,
header: false,
disabled: j % 9 === 0
});
}
}
this.items = itemsCollection;
}
}
Styles
A última parte da configuração é definir overflow: hidden
para o div de encapsulamento para evitar o aparecimento de duas barras de rolagem (uma do igxFor
e uma do próprio contêiner):
// drop-drop-virtual.component.scss
.drop-down-virtual-wrapper {
overflow: hidden;
}
Remote Data
O igx-drop-down
suporta o carregamento de pedaços de dados remotos usando a diretiva estrutural *igxFor
. A configuração é similar à dos itens locais, a principal diferença é como os pedaços de dados são carregados.
Template
O modelo drop-down não precisa mudar muito em comparação ao exemplo anterior - ainda precisamos especificar um div de encapsulamento, estilizá-lo adequadamente e escrever a configuração completa para o *igxFor
. Como obteremos nossos dados de uma fonte remota, precisamos especificar que nossos dados serão um observable e passá-los pelo pipe async
do Angular:
<igx-drop-down #remoteDropDown>
<div class="drop-down-virtual-wrapper">
<igx-drop-down-item
*igxFor="let item of rData | async; index as index;
scrollOrientation: 'vertical';
containerSize: itemsMaxHeight;
itemSize: itemHeight;"
[value]="item.ProductName" role="option"
[disabled]="item.disabled" [index]="index">
{{ item.ProductName }}
</igx-drop-down-item>
</div>
</igx-drop-down>
Handling chunk load
Como você pode ver, o template é quase idêntico ao do exemplo anterior. Neste cenário de dados remotos, o código por trás fará a maior parte do trabalho pesado.
Primeiro, precisamos definir um serviço remoto para buscar dados:
// remote.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { IForOfState } from 'igniteui-angular';
// import { IForOfState } from '@infragistics/igniteui-angular'; for licensed package
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable()
export class RemoteService {
public remoteData: Observable<any[]>;
private _remoteData: BehaviorSubject<any[]>;
constructor(private http: HttpClient) {
this._remoteData = new BehaviorSubject([]);
this.remoteData = this._remoteData.asObservable();
}
public getData(data?: IForOfState, cb?: (any) => void): any {
// Assuming that the API service is RESTful and can take the following:
// skip: start index of the data that we fecth
// count: number of records we fetch
this.http.get(`https://dummy.db/dummyEndpoint?skip=${data.startIndex}&count=${data.chunkSize}`).subscribe((data) => {
// emit the values through the _remoteData subject
this._remoteData.next(data);
})
}
O serviço expõe um Observable
sob remoteData
. Injetaremos nosso serviço e vincularemos a essa propriedade em nosso componente drop-down remoto:
// remote-drop-down.component.ts
@Component({
providers: [RemoteService],
selector: 'app-drop-down-remote',
templateUrl: './drop-down-remote.component.html',
styleUrls: ['./drop-down-remote.component.scss']
})
export class DropDownRemoteComponent implements OnInit, OnDestroy {
@ViewChild(IgxForOfDirective, { read: IgxForOfDirective })
public remoteForDir: IgxForOfDirective<any>;
@ViewChild('remoteDropDown', { read: IgxDropDownComponent })
public remoteDropDown: IgxDropDownComponent;
public itemHeight = 48;
public itemsMaxHeight = 480;
public prevRequest: Subscription;
public rData: any;
private destroy$ = new Subject();
constructor(private remoteService: RemoteService) { }
public ngAfterViewInit() {
const initialState = { startIndex: 0, chunkSize: Math.ceil(this.itemsMaxHeight / this.itemHeight) }
this.remoteService.getData(initialState, (data) => {
this.remoteForDir.totalItemCount = data['@odata.count'];
});
// Subscribe to igxForOf.chunkPreload and load new data from service
this.remoteForDir.chunkPreload.pipe(takeUntil(this.destroy$)).subscribe((data) => {
this.dataLoading(data);
});
}
public dataLoading(evt) {
if (this.prevRequest) {
this.prevRequest.unsubscribe();
}
this.prevRequest = this.remoteService.getData(
evt,
(data) => {
this.remoteForDir.totalItemCount = data['@odata.count'];
});
}
public ngOnInit() {
this.rData = this.remoteService.remoteData;
}
public ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
Dentro do ngAfterViewInit
gancho, chamamos para obter dados para o estado inicial e assinar o igxForOf
diretivas chunkPreload
emissor. Esta assinatura será responsável por buscar dados toda vez que o bloco carregado mudar. Usamos pipe(takeUntil(this.destroy$))
para que possamos facilmente cancelar a assinatura do emissor na destruição do componente.
Remote Virtualization - Demo
O resultado da configuração acima é um menu suspenso que carrega dinamicamente os dados que deve exibir, dependendo do estado da barra de rolagem:
Notes and Limitations
Usar o drop-down com uma lista virtualizada de itens impõe algumas limitações. Por favor, esteja ciente do seguinte ao tentar configurar uma lista drop-down usando *igxFor
:
- Os itens suspensos que estão sendo repetidos precisam ser passados em um elemento de encapsulamento (por exemplo,
<div>
) que tenha o seguinte css:overflow: hidden
eheight
igual acontainerSize
empx
<igx-drop-down-item-group>
não pode ser usado para agrupar itens quando a lista é virtualizada. Use a propriedadeisHeader
em vez disso- O acessador
items
retornará apenas a lista de itens suspensos sem cabeçalho que estão atualmente na exibição virtualizada. dropdown.selectedItem
é do tipo{ value: any, index: number }
- O objeto emitido pela
selection
muda paraconst emittedEvent: { newSelection: { value: any, index: number }, oldSelection: { value: any, index: number }, cancel: boolean, }
dropdown.setSelectedItem
deve ser chamado com o índice do item no conjunto de dados- definir a entrada
[selected]
do item suspenso não marcará o item na seleção suspensa