0

I am writing a generic FilterListView component. Currently when using it you can pass in a TemplateRef that dictates how each element is displayed, alongside filtering options etc. This works great when used as is, but there is often need of reuse on various pages which makes this cumbersome

Example of how it currently works
<filter-list-view icon='xxx'
                  [templateRef]="myRef"
                  [items]="myItems" 
                  [filterPredicate]="filterPredicate">
    <ng-template #myRef let-item>
        {{ item.Name }}
    </ng-template>
</filter-list-view>
How I would like it to work

Ideally I would like to have a service per type of filter (e.g. 'user', 'resource' etc) that specifies how to collect the data, how to filter the items, the component needed to display results (e.g. templateRef) etc

<filter-list-view [filterDefinition]="userFilterService"></filter-list-view

Where UserFilterService might look like this:

export class UserFilterService implements IFilterService<T> 
{
   icon: string;
   items: T[];
   dataCollection: Promise<T[]>;
   filterPredicate: (T) => boolean;
   resultComponent: any; // this would hold the TYPE of the component used for display
}

What I am struggling with, is a way to either create a TemplateRef dynamically to use in lieu of #myRef which is passed in to templateRef in the first example.

Ideally UserFilterService would, create or provide a TemplateRef to FilterListView to allow it to render results dynamically.

What have I tried? Dynamic TemplateRef.

I initially tried to create a TemplateRef dynamically using this but it just didnt work. In this case I get an odd error

BrowserModule has already been loaded. If you need access to common directives such as NgIf and NgFor from a lazy loaded module, import CommonModule instead.

This error points to line 45 in the template compiler in the stackblitz referenced above. That is called by my code here.

var html = `<my-item-result [myBinding]="item"></my-item-result>`;
var ref = this.templateCompiler.compile(html, [MyItemResultModule])

ref.subscribe(
    x => {
        debugger;
        this.itemTemplate = x;
    },
    err => {
        console.error(err);
    });

I have BrowserModule only listed once in my app, and that is in the main entry module to the app. So this seems odd?

What have I tried? Dynamic Components.

Then I tried switching tactic, instead trying to create the Component directly, and attaching it to an ng-template within my FilterListView.html as per this answer. This worked for the most part, but I couldnt get the component I created to bind to the let-item within the host ng-template.

Chris
  • 26,744
  • 48
  • 193
  • 345
  • Is the template really dynamic ? Shipping the compiler in prod is not recommanded. – Matthieu Riegler Jan 19 '23 at 16:46
  • 1
    So you are trying to create a reusable filter-list-view, where instead of defining the layout using a ng-template, you pass it a reference to something which has a component for it to create? – Mikkel Christensen Jan 19 '23 at 17:02
  • @MikkelChristensen I dont mind how it works, I would just like to be able to define specific use cases (such as user filtering etc) that are used often, that leverages the existing component. I dont want a user to have to define a new ng-template everytime it is used – Chris Jan 19 '23 at 21:11
  • What you want to achieve is likely very possible using a mix of either `@ViewChild`, `@ContentChild` or using structural directives, but I can't really help you with a specific answer because I can't figure out from your post what exactly it is you are trying to achieve. If you are happy with the current behavior, except you have cases where you need to redo the same template over and over, why don't you just create a normal component that defines this behavior by wrapping the generic one? – Mikkel Christensen Jan 21 '23 at 19:26

0 Answers0