0

I need to create a component which takes any number of links <a>link</a> and structure the design into two groups.

  1. first 2 or 3 links (based on an input parameter) visible inline.
  2. second group should be in a menu.

I solved the issue by creating a class that contains all the needed parameters, then separating the list of links into two separate arrays, and by using ngIf I managed to get the results, but the code does not look clean, and harder to use.

What I'm trying to achieve is something like below: custom component:

@Component({
    selector: 'my-dynamic-links',
    templateUrl: './custom-component.html'
})
CustomComponent {
    @Input() noOfVisibleLinks: number;
}

parent html

<my-dynamic-links noOfVisibleLinks="3">
    <a class="btn btn-primary" href="#" role="button">Link 1</a>
    <a class="btn btn-primary" href="#" role="button">Link 2</a>
    <a class="btn btn-primary" href="#" role="button">Link 3</a>
    <a class="btn btn-primary" href="#" role="button">Link 4</a>
    <a class="btn btn-primary" href="#" role="button">Link 5</a>
    <a class="btn btn-primary" href="#" role="button">Link 6</a>
    <a class="btn btn-primary" href="#" role="button">Link 7</a>
</my-dynamic-links>

it should look like this:

enter image description here

2 Answers 2

2

You want to use content projection, as you're doing, but move the "count" logic in the parent Component.

<my-dynamic-links>
   <a class="inline" ...></a>
   <a class="inline" ...></a>
   <a class="inline" ...></a>

   <a class="menu" ...></a>
   <a class="menu" ...></a>
</my-dynamic-links>

And in the custom-component.html template, you can use the ng-content element select attribute to split the links in two groups

<ng-content select="a.inline"></ng-content>
...
<ng-content select="a.menu"></ng-content>

If you still want the flexibility, just conditionally add the inline or menu class

<my-dynamic-links>
   <a class="{{ useClass(0) }}" ...></a>
   <a class="{{ useClass(1) }}" ...></a>
   <a class="{{ useClass(2) }}" ...></a>

   <a class="{{ useClass(3) }}" ...></a>
   <a class="{{ useClass(4) }}" ...></a>
</my-dynamic-links>

Or simply using an *ngFor structural Directive

<my-dynamic-links>
   <a *ngFor="let i of total | times" class="{{ useClass(i) }}" ...></a>
</my-dynamic-links>

// times is a custom Pipe which returns an Iterable.

And

useClass(index: number): string {
  return this.noOfVisibleLinks > index ? 'inline' : 'menu';
}

You can do this in many different ways.


Another one, using @ContentChildren(...).
In your CustomComponent

@ContentChildren(LinkDirective)
links!: QueryList<LinkDirective>;

...

ngAfterViewInit(): void {
  this.links.forEach((linkDirective, index) => linkDirective.applyInline(this.noOfVisibleLinks > index));
}

And in the parent Component

<my-dynamic-links [noOfVisibleLinks]="noOfVisibleLinks">
   <a link ...></a>
   <a link ...></a>
   <a link ...></a>
   <a link ...></a>
   <a link ...></a>
</my-dynamic-links>
Sign up to request clarification or add additional context in comments.

7 Comments

but this wont give me the flexibility to update the number of visible links dynamically for the entire application without changing the html. because I can override the input parameter from a config file. I'm not sure if it possible to do it or not. but I need to pass as many links and maintaining the logic inside the customComponent
@IBRA you can dynamically change the inline or menu class with an ngFor, or a simple template expression, for example. Remember you still have the value you need inside the parent component.
@IBRA than I'd say the ngFor way isn't that bad. I have nothing more to offer, or it would be too hacky.
you're exactly right, but ngFor wont make a big difference with my current solution, since the actual code i have is more complex and have different values and business rules for each button. I was looking for something cleaner and easy to use by other developers. if I couldn't find anything else i guess i'm going to stick with whatever i have now. thank you, and appreciate your effort and time :)
@IBRA no problem. If you come up with something, let me know!
|
1

If I get your question correctly this is what you perhaps need to do: Create an array like this in the parent component:

    [{
        link: 'link1',
        visible: 'inline'
    },
    {
        link: 'link2',
        visible: 'menu'
    },
    {
        link: 'link3',
        visible: 'menu'
    }
    ]

Pass it to the component you have created. Filter and create two arrays, one for the inline links and the other for menu links.

In the template loop through the arrays again and show the inline links or the menu. You can even create two more child components for showing the links inline or in a menu and pass the HTML links using ng-content.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.