Within a Blueriq theme, each Blueriq page element needs to be shown on the page in some manner, depending on the characteristics of the element, such as the type of element or the presentation style it contains. This is where Angular components come into play, with additional metadata for the Blueriq framework to use.
A typical Angular component needs to be decorated with the @BlueriqComponent
decorator that specifies under what conditions the component should be chosen as visual representation of an element. As an example, a component to show the readonly representation of a field element would look as follows:
In @BlueriqComponent
, it is always required to specify the type of the element for which the component is applicable. Notice that Field
in above example is included from @blueriq/core
. The selector property is optional and is similar to an Angular Component's selector, the only difference is that a Blueriq selector targets the Blueriq page tree instead of an HTML element. The selector allows you to specify additional characteristics that the element must have in order for this component to be used.
In above example, the
@Component
decorator also specifies a selector. Because the component is always rendered dynamically by the Blueriq framework you may choose to omit it entirely. It does however affect the rendered DOM, as Angular will reflect the selector in the name of the element that it creates as host element for the dynamic component. If you were to omit the selector, Angular will always useng-component
as name of the host element.
In the constructor, you can let Angular inject the Blueriq element that belongs to the component instance by simply requesting it through the element type. This may also be done in Angular directives, as they have access to the same services as a component. In directives that are generic for any kind of Blueriq element, inject Element
from @blueriq/core
instead of a concrete element type.
A Blueriq selector closely resembles a typical CSS selector, where each concept in CSS has been translated to work with the Blueriq element tree. In below table an overview of supported selectors is given.
Selector | Description |
---|---|
dashboard_flowwidget |
Matches on content style equal to 'dashboard_flowwidget' |
.icon |
Matches on presentation style containing 'icon' |
#container |
Matches on element with name 'container' |
[multiValued] |
Matches on a 'multiValued' property that is truthy |
[dataType=boolean] |
Matches on the 'dataType' property equal to 'boolean' |
[contentStyle^=bool] |
Matches on content style starting with 'bool' |
[contentStyle$=bool] |
Matches on content style ending with 'bool' |
[contentStyle*=bool] |
Matches on content style containing 'bool' |
[domain-size<=2] |
Matches on a field with less than 3 domain values |
[domain-size>=3] |
Matches on a field with at least 3 domain values |
table * |
Matches on any element having an ancestor with content style 'table' |
table row |
Matches on content style 'row' having an ancestor with content style 'table' |
table > row |
Matches on content style 'row' having an immediate parent with content style 'table' |
.icon:not(.large) |
Matches on presentation style 'icon' but not having presentation style 'large' |
:has(.icon) |
Matches if a descendant with presentation style 'icon' exists |
:has(* > .icon) |
Matches if a direct child with presentation style 'icon' exists |
You may specify multiple selectors by using a comma as separator, equal to normal CSS.
Next to the declaration of a Blueriq component using the @BlueriqComponent
decorator, it must separately be registered as Blueriq component in an Angular module using BlueriqComponents
:
In the above example, notice how all Blueriq components are listed in the constant BLUERIQ_COMPONENTS
, which is used in the module's declarations and in providers. This approach avoids a situation where you forget to add a new component in only one place.
If you register a component without
@BlueriqComponent
decorator, a warning will be logged in the browser.
The ordering of components is important, as the framework will simply select the first component declaration that matches a given element. Hence, the framework first sorts all registered components by a numeric priority value that has been computed from the component selectors. The more specific the selector, the higher its priority will be.
If you encounter a situation where components have equal priority but their mutual ordering is important, you have several options. The component that has been registered first is guaranteed to be ordered first, so changing the registration order may resolve the discrepancy. As an alternative, consider making one of the selectors more specific for its priority to increase. As a last resort, you may influence the priority computation by providing a priority offset with the bySelector
function from @blueriq/angular
:
Using
bySelector
will cause the selector to be eagerly parsed. If the selector is invalid, the application will fail to start instead of logging the parse failure and ignoring the faulty component.
Consider the scenario where you want to use custom field components within table rows. You could include a parent constraint in your selector, for example table [dataType=string]
would only match fields that are contained within an element with content style table
. This however is suboptimal, as you would 1) have to repeat the parent constraint for each individual component, 2) lose the connection between the table component and its custom children, and 3) negatively affect performance by having a larger number of components to consider for each element on the page. These downsides can be avoided by registering the custom elements from the table component as scoped components.
Scoped components are not registered globally, but locally at component level. Local components always take precedence over globally registered components, regardless of priority. To register a scoped component, add providers
to the @Component
decorator and use BlueriqComponents.scoped([...])
with a list of component classes.
Scoped components will not be shown in the components table during startup, as that table only shows the globally registered components.
Scoped components still need to be included in an Angular module in
declarations
, but should not be registered with Blueriq usingBlueriqComponents.register
as that is for global registration.
We recommend to always use
bySelector
in components that are locally registered, as otherwise the selector has to be parsed each time it comes into scope.
It may occur that a module you include registers a component for which you want to alter its behavior, for example. This is possible through component specialization using BlueriqComponents.specialize
:
With above module imported in your application, anytime the ReadonlyComponent
is chosen the framework will use SpecializedReadonlyComponent
instead. The SpecializedReadonlyComponent
class does not need to have a BlueriqComponent
decorator, as component resolution has already taken place at this point.
Any registered specialization will even be used for scoped components that have been registered locally on a component.
Now that we have been able to register components, we need to actually render them from somewhere in our templates. For this purpose the Angular directive bqElement
exists, which accepts a Blueriq element and determines the appropriate component type to render dynamically. A typical usage would be iterating over all page/container children:
The usage of the
bqIncluded
pipe ensures that any element that has been excluded will be skipped over. For more details on what that means exactly, please refer to the querying guide.
Notice that Angular's ngFor
directive is applied to an ng-container
. This is a special kind of element for which no DOM element is rendered. If you need to render the component within an actual DOM element, replacing the ng-container
with the desired element tag will not have the desired effect. The reason is that components rendered by bqElement
will be inserted as sibling instead of as child, so we would need to change the structure a bit:
To use
bqElement
andbqIncluded
in feature modules, ensure thatBlueriqCommonModule
is imported.
The most common session interaction in a Blueriq application is handling form field refresh events and button presses. These actions are exposed by the BlueriqSession
service that is made available within bq-session
:
As noted in the above example, button clicks are rather handled using the bqButton
directive in a template, as it avoids custom code in the component at all. Within a Blueriq component for a Button
element, simply add the bqButton
attribute without a value to an element for click events to be handled.
In the case where you want to tell bqButton
to use a different button, bind its value to a Button
instance instead:
To use
bqButton
in feature modules, ensure thatBlueriqCommonModule
is imported.