Ligação remota do ComboBox
O componente Ignite UI for Angular ComboBox expõe uma API que permite vincular uma combobox a um serviço remoto e recuperar dados sob demanda.
Angular ComboBox Remote Binding Example
O exemplo abaixo demonstra a vinculação remota usando a propriedade dataPreLoad para carregar um novo bloco de dados remotos:
Usage
Para começar a usar o componente ComboBox, primeiro você precisa importar o IgxComboModule
no seu arquivo app.module.ts. Nesta demonstração, um serviço remoto é usado para solicitações de servidor, portanto, também precisamos incluir o HttpClientModule
:
import { IgxComboModule } from 'igniteui-angular';
// import { IgxComboModule } from '@infragistics/igniteui-angular'; for licensed package
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
...
IgxComboModule,
HttpClientModule,
...
]
})
export class AppModule {}
Define Remote Service
Ao vincular um combobox a dados remotos, precisamos ter um serviço disponível que carregará dados sob demanda de um servidor. O componente combobox expõe a propriedade virtualizationState que fornece o estado atual de um combobox - o primeiro índice e o número de itens que precisam ser carregados. Para mostrar corretamente o tamanho do scroll, a propriedade totalItemCount deve ter um valor que corresponda ao total de itens no servidor.
O código abaixo define um serviço simples que possui um método getData()
, que recebe informações do estado atual do combobox e retorna dados como um observável:
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();
}
// Use combobox current virtualization state and search text to build URL and request the new data.
public getData(data?: IForOfState, searchText?: string, cb?: (any) => void): any { }
}
Binding ComboBox to Remote Service
Quando os dados são retornados de um serviço como um observável, podemos defini-los no componente combobox usando o pipe assíncrono:
<igx-combo [data]="rData | async"
[valueKey]="'ProductID'"
[displayKey]="'ProductName'"
(dataPreLoad)="dataLoading($event)"
(searchInputUpdate)="handleSearchInputUpdate($event)"
(selectionChanging)="handleSelectionChanging($event)"
(closing)="onClosing()"
(opened)="onOpened()"
(closed)="onClosed()"
[filterable]="true">
</igx-combo>
Aqui estão alguns casos comuns quando o componente combobox precisa solicitar novos dados: - quando o combobox é inicializado - quando rolamos a lista do combobox - ele emitirá dataPreLoad
junto com o novo combobox virtualizationState
, o que permite fazer uma nova solicitação ao serviço remoto. - ao pesquisar em um combobox - precisamos fazer uma solicitação para filtrar resultados remotos. - quando o combobox é aberto - precisamos limpar os resultados de quaisquer operações de filtro anteriores.
Abaixo estão listados os manipuladores que escutam as ações já definidas e executam requisições ao servidor:
import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core';
import { IgxComboComponent } from 'igniteui-angular';
// import { IgxComboComponent } from '@infragistics/igniteui-angular'; for licensed package
import { RemoteService } from '../../grid/services/remote.service';
@Component({
providers: [RemoteService],
selector: 'app-combo-remote',
styleUrls: ['./combo-remote.component.scss'],
templateUrl: './combo-remote.component.html'
})
export class ComboRemoteComponent implements OnInit {
@ViewChild('remoteCombo', { read: IgxComboComponent }) public remoteCombo: IgxComboComponent;
public prevRequest: any;
public rData: any;
private searchText: string = null;
private defaultVirtState: IForOfState = { chunkSize: 6, startIndex: 0 };
private currentVirtState: IForOfState = { chunkSize: 6, startIndex: 0 };
private itemID: number = 1;
private itemCount: number = 0;
private hasSelection: boolean;
private additionalScroll: number = 0;
constructor(private remoteService: RemoteService, public cdr: ChangeDetectorRef) { }
public ngOnInit() {
this.rData = this.remoteService.remoteData;
}
public ngAfterViewInit() {
const initSize = {
startIndex: 0,
chunkSize: Math.ceil(250 / this.remoteCombo.itemHeight)
};
this.remoteService.getData(initSize, null, (data) => {
this.remoteCombo.totalItemCount = data['@odata.count'];
this.itemCount = this.remoteCombo.totalItemCount;
});
}
public dataLoading(evt) {
if (this.prevRequest) {
this.prevRequest.unsubscribe();
}
this.prevRequest = this.remoteService.getData(
this.remoteCombo.virtualizationState,
this.searchText,
(data) => {
this.remoteCombo.totalItemCount = data['@odata.count'];
this.cdr.detectChanges();
});
}
public handleSearchInputUpdate(searchData: IComboSearchInputEventArgs) {
this.currentVirtState.startIndex = 0;
this.currentVirtState.chunkSize = Math.ceil(this.remoteCombo.itemsMaxHeight / this.remoteCombo.itemHeight);
this.searchText = searchData?.searchText || '';
this.remoteService.getData(
this.searchText ? this.currentVirtState : this.defaultVirtState,
this.searchText,
(data) => {
this.remoteCombo.totalItemCount = data['@odata.count'];
}
);
}
public onOpened() {
const scroll: number = this.remoteCombo.virtualScrollContainer.getScrollForIndex(this.itemID - 1);
this.remoteCombo.virtualScrollContainer.scrollPosition = scroll + this.additionalScroll;
this.cdr.detectChanges();
}
public onClosing() {
this.searchText = '';
}
public onClosed() {
this.currentVirtState.startIndex = (this.itemID || 1) - 1;
this.remoteService.getData(
this.currentVirtState,
this.searchText,
(data) => {
this.remoteCombo.totalItemCount = data['@odata.count'];
this.cdr.detectChanges();
}
);
}
public handleSelectionChanging(evt: IComboSelectionChangingEventArgs) {
this.hasSelection = !!evt?.newSelection.length;
if (!this.hasSelection) {
this.itemID = 1;
this.currentVirtState = this.defaultVirtState;
return;
}
const currentSelection = evt.newSelection[evt.newSelection.length - 1]
this.currentVirtState.chunkSize = Math.ceil(this.remoteCombo.itemsMaxHeight / this.remoteCombo.itemHeight);
this.itemCount === currentSelection ?
this.additionalScroll = this.remoteCombo.itemHeight :
this.additionalScroll = 0;
if (this.itemCount - currentSelection >= this.currentVirtState.chunkSize - 1) {
this.itemID = this.currentVirtState.startIndex = currentSelection;
} else {
this.itemID = this.currentVirtState.startIndex = this.itemCount - (this.currentVirtState.chunkSize - 1);
}
}
}
Note
Anytime new data is loaded, we update the totalItemCount
property, in order to have proper size of the list's scroll bar. In that case, the service returns total size using the property @odata.count
.
Note
Um serviço precisa ser incluído como provedor.
Handling Selection
Ao usar um combobox vinculado a dados remotos carregados em blocos e lidar com um tipo de dado mais complexo (por exemplo, objetos), é necessário definir um valueKey
. Conforme declarado no tópico combobox, quando nenhum valueKey
é especificado, o combobox tentará manipular a seleção por equality (===)
. Como os objetos que serão marcados como selecionados não serão os mesmos que os objetos que são carregados continuamente, a seleção falhará.
Note
Ao vincular uma caixa de combinação a dados remotos, certifique-se de especificar uma valueKey
, representando uma propriedade exclusiva de cada item.
Quando o combobox estiver vinculado a dados remotos, definir valor/itens selecionados por meio da API levará em conta apenas os itens que são carregados no bloco atual. Se você quiser definir um valor inicial, certifique-se de que esses itens específicos sejam carregados antes de selecionar.
API Summary
Additional Resources
- Componente ComboBox
- Recursos do ComboBox
- Modelos de ComboBox
- Integração de formulários orientados a modelos
- Integração de Formulários Reativos
- ComboBox de seleção única
Nossa comunidade é ativa e sempre acolhedora para novas ideias.