Hello ABP Team,
I am working with ABP extensible forms, and I’m using a custom template/component to override the rendering of certain fields via the [templates]
property on <abp-extensible-form>
. My custom component injects the field config and record data using:
constructor(
@Inject(EXTENSIONS_FORM_PROP) public prop: FormProp,
@Inject(EXTENSIONS_FORM_PROP_DATA) public propData: any
) {}
My issue/question:
- When using the default ABP rendering, the options function for typeahead and other extensible fields receives a rich
PropData
object (withrecord
and sometimes helpers likegetInjected
). - However, in my custom template component,
propData
is just the record (row/entity) and does not include any extra context or helper functions. - This means that my field’s
options
function has less information than it does in the default scenario.
Questions:
- Is there a supported way to access the full
PropData
(with helpers and full context) inside a custom template/component (e.g., for use in options or validators)? - Is it possible to have
EXTENSIONS_FORM_PROP_DATA
provide the full context instead of only therecord
when using template overrides? - If not, is there a recommended pattern to pass additional context to custom field templates, so options/validators can use other injected services or helpers as in the default renderer?
- Are there plans to support this use-case in a future version?
Any best practices, workarounds, or guidance would be greatly appreciated.
Thank you!
@Component({
selector: 'custom-typeahead-field',
template: ` ... `
})
export class CustomTypeaheadFieldComponent {
constructor(
@Inject(EXTENSIONS_FORM_PROP) public prop: FormProp,
@Inject(EXTENSIONS_FORM_PROP_DATA) public propData: any
) {}
// ...
search = (params: PagedAndSortedResultRequestDto): Observable<PagedResultDto<any>> => {
if (this.prop?.options) {
return this.prop.options(this.propData, params).pipe(
switchMap((result: any) => {
if (Array.isArray(result)) {
return of({ items: result, totalCount: result.length });
}
return of(result);
})
);
}
return of({ items: [], totalCount: 0 });
};
}
{
type: ePropType.Typeahead,
name: 'propId',
displayName: 'propName',
validators: () => [],
options: (data$, params) => {
const manufacturerService = data$.getInjected(MyService);
return myService
.getList({
filter: params ?? '',
maxResultCount: params?.maxResultCount,
skipCount: params?.skipCount,
})
.pipe(
map(result => {
//add empty value
return result.items.map(item => ({ value: item.id, key: item.name }));
})
);
},
template: MyWrapperComponent,
},
**ERROR TypeError: data$.getInjected is not a function**
Thank you, David Simões
1 Answer(s)
-
0
Hello David,
Thank you for the detailed ticket and context around your use of custom templates with the extensible forms. Let me address your questions as well.
When using custom components inside extensible forms or tables, the injected
EXTENSIONS_FORM_PROP_DATA
only provides the plainrecord
by default, not the fullPropData
with helpers likegetInjected
.Although
PropData
definesgetInjected(...)
, it's only populated in certain rendering contexts (e.g., the default ABP flow). When creating custom components, this context isn’t automatically passed.If you're overriding rendering via custom components (like
template: MyComponent
inside prop config), you're responsible for providing the full context (record + helpers) if needed.{ provide: EXTENSIONS_FORM_PROP_DATA, deps: [Injector], useFactory: (injector: Injector) => ({ record, getInjected: <T>(token: any) => injector.get<T>(token) }) }
As for future support, we’ll share feedback with the team — but for now, the pattern above is the recommended way to ensure full context is available in custom components.
I am also sharing a sample project showing how I replicated your structure based on these details: https://drive.google.com/file/d/1qLoFopObla_kGQ1k_a71KVvrlMZ_DPBc/view?usp=drive_link
If you think that this does not answer your question, I can assist you further. Thank you for your cooperation.