Querying

Within components you may sometimes need access to a specific child element. To accomplish this, the framework allows for declaratively querying for elements, inspired by Angular's @ViewChild/@ViewChildren approach.

Singular Result

When only a single element is expected, use the @BlueriqChild decorator on a field:

import { Component } from '@angular/core';
import { BlueriqComponent, BlueriqChild } from '@blueriq/angular';
import { Field } from '@blueriq/core';
import { Observable } from 'rxjs';

@Component({})
@BlueriqComponent({})
export class ContainerComponent {

  @BlueriqChild(Field)
  field: Field;

  @BlueriqChild(Field, '[dataType=text]')
  textField: Field;

  @BlueriqChild(Field, { descendants: true })
  descendingField: Field;

  @BlueriqChild(Field, { optional: true })
  requiredField: Field | undefined;

  @BlueriqChild(Field, { observe: true })
  field$: Observable<Field>;

}

The property bindings will be kept in sync as elements come and go. Please note that the properties will not be available inside of the constructor, as the queries have not been evaluated at that time. In any of Angular's lifecycle hooks you will however be able to access the query results.

Optionally, a Blueriq selector is accepted as second argument, in between the element type and optional options object. The options are as follows:

  • optional
    default: false
    When querying for a single element, it is assumed that the element must be present at all times. In the situation that no element could be found, a descriptive error will be thrown. If you need to support the case where no element was found, set this option to true in which case the property may also receive an undefined value. Therefore, we recommend to include undefined in the type specification as shown in the example above to make it obvious that all code paths have to deal with the element being possibly undefined. By enabling TypeScript's strictNullChecks option, the TypeScript compiler will guard against unchecked usage of the property.
  • descendants
    default: false
    Also include all descendants during the search, not only direct children.
  • observe
    default: false
    Binds the property with an Observable that you may subscribe to to receive updates. Upon subscription you will immediately receive the most recent query result. It is not guaranteed that the resulting element has actually changed when the observable emits.
  • exclude
    default: false
    Specifies whether or not the element that is found should be excluded, such that the bqIncluded pipe will skip over it when rendering children. This is useful in the situation where you need to extract a certain element out of the page but still need to render all other children as usual, in which case the excluded element will be skipped over.

Multiple Results

When multiple elements are expected, use the @BlueriqChildren decorator on a field:

import { Component, QueryList } from '@angular/core';
import { BlueriqComponent, BlueriqChildren } from '@blueriq/angular';
import { Field } from '@blueriq/core';
import { Observable } from 'rxjs';

@Component({})
@BlueriqComponent({})
export class ContainerComponent {

  @BlueriqChildren(Field)
  fields: QueryList<Field>;

  @BlueriqChildren(Field, '[dataType=text]')
  textFields: QueryList<Field>;

  @BlueriqChildren(Field, { descendants: true })
  descendingFields: QueryList<Field>;

  @BlueriqChildren(Field, { required: true })
  requiredFields: QueryList<Field>;

  @BlueriqChildren(Field, { observe: true })
  fields$: Observable<Field[]>;

}

The property bindings will be kept in sync as elements come and go. Please note that the properties will not be available inside of the constructor, as the queries have not been evaluated at that time. In any of Angular's lifecycle hooks you will however be able to access the query results.

Optionally, a Blueriq selector is accepted as second argument, in between the element type and optional options object. The options are as follows:

  • required
    default: false
    By default, the property receives an empty array when no matching element could be found. If you do require that at least a single element is found, set this option to true in which case a descriptive exception will be thrown when no matching elements can be found.
  • descendants
    default: false
    Also include all descendants during the search, not only direct children.
  • observe
    default: false
    Binds the property with an Observable that you may subscribe to to receive updates. Upon subscription you will immediately receive the most recent query results as an array. It is not guaranteed that the resulting elements have actually changed when the observable emits.
  • exclude
    default: false
    Specifies whether or not the elements that are found should be excluded, such that the bqIncluded pipe will skip over when rendering children. This is useful in the situation where you need to extract certain elements out of the page but still need to render all other children as usual, in which case the excluded elements will be skipped over.

Excluding elements

As briefly discussed above in the available options, the querying mechanism allows for excluding an element. Together with the bqIncluded pipe, an element that has been excluded will be skipped from rendering. This is useful in the situation where you need to extract a certain element from the page to render differently from other elements, while still needing to show all other children normally.

The bqElement directive will render an element even in the case it has been excluded. This is by design, as it allows to query for an element in order to show it in a custom location, but still use its dedicated component.

For an excluded element to be actually skipped from rendering, the bqIncluded pipe must be used everywhere when rendering an element's children.

Querying in Services

The framework is only able to automatically identify @BlueriqChild/@BlueriqChildren decorators in Blueriq components. Angular services that want to use @BlueriqChild/@BlueriqChildren need to attach themselves to the querying engine manually:

import { Injectable, OnDestroy } from '@angular/core';
import { BlueriqChild, BlueriqQuerying } from '@blueriq/angular';
import { Field } from '@blueriq/core';

@Injectable()
export class Pagination implements OnDestroy {

  @BlueriqChild(Field)
  field: Field;

  constructor(private querying: BlueriqQuerying) {
    this.querying.attach(this);

    // this.field is now available
  }

  ngOnDestroy(): void {
    this.querying.detach(this);
  }

}

In contrast to components, ngOnInit is not called for Angular services. As shown in the example above, the queried properties will be available immediately after you have attached the querying engine.

You must take care of detaching the service from the query engine when the service is destroyed, otherwise memory leaks will occur.

Note that BlueriqQuerying service is only present within the component tree and not in services provided by an Angular module. Hence, such services must be provided by components themselves:

import { Component, Self } from '@angular/core';
import { BlueriqComponent } from '@blueriq/angular';
import { Pagination } from '@blueriq/angular/lists';
import { Container } from '@blueriq/core';

@Component({
  templateUrl: './pagination.component.html',
  providers: [Pagination],
})
@BlueriqComponent({
  type: Container,
  selector: 'pagination',
})
export class PaginationComponent {

  constructor(@Self() public pagination: Pagination) {}

}

By default, the injected BlueriqQuerying service is bound to the current Blueriq element to start querying from, corresponding with the Container in above example. If you need to query from a different element, pass it as secondary argument when attaching.

Structure Services

With the ability to let Angular services query the Blueriq element tree, we recommend complex Blueriq tree structures to be represented by "structure services" that are reusable across components. These services may then contain logic to transform the Blueriq element tree into a representation that better fits the view.

The Blueriq framework ships with structure services for tables and pagination structures.

result-matching ""

    No results matching ""