algolia / angular-instantsearch

⚡️Lightning-fast search for Angular apps, by Algolia
https://algolia.com/doc/deprecated/instantsearch/angular/v4/api-reference/instantsearch/
MIT License
261 stars 73 forks source link

Consider to always provide a template with exposed props #378

Closed samouss closed 3 years ago

samouss commented 5 years ago

Right now the current process to customise a component is a bit cumbersome. We can leverage some Angular feature to have the same kind of API that we have with Vue InstantSearch (with slots). Here is a trivial example with a Toggle component (see below). With this API we don't have to manually create a component with the connector, etc... We can directly override the output from the template, it's very similar to the slots in Vue and render props in React.

Disclaimer: not sure that it's the "standard" way to let the user have the full control on the render.


Implementation:

import { Component, ContentChild, TemplateRef } from '@angular/core';

@Component({
  selector: 'app-toggle',
  template: `
    <ng-container
      *ngTemplateOutlet="
        template ? template : defaultToggleTemplate;
        context: { on: state, toggle: toggle }
      "
    ></ng-container>

    <ng-template #defaultToggleTemplate let-state="on" let-toggle="toggle">
      <div>inner = '{{ state }}'</div>
      <button (click)="toggle()">Toggle</button>
    </ng-template>
  `,
})
export class ToggleComponent {
  @ContentChild(TemplateRef) public template?: TemplateRef<any>;

  state = false;

  toggle = () => {
    this.state = !this.state;
  };
}

Usage:

<app-toggle></app-toggle>

<app-toggle>
  <ng-template let-state="on" let-toggle="toggle">
    <p>This is a custom render of the component.</p>
    <label>
      <input type="checkbox" (change)="toggle()" hidden />
      <span>{{ state ? '✅' : '❌' }}</span>
      <span>The input is {{ state ? 'checked' : 'unchecked' }}</span>
    </label>
  </ng-template>
</app-toggle>

Here is the live example on CodeSandbox.

tkrugg commented 5 years ago

It totally makes sense. So would this replace entirely the need for BaseWidget?

samouss commented 5 years ago

Internally or externally? Because we still have to extends from it internally to have access to the InstantSearch instance.

tkrugg commented 5 years ago

I meant externally. I was thinking not advertising its usage at all. Reading the documentation I think maybe it's still the answer to some complex use cases (eg. overriding a refine method)

nevermind. Sounds good. I'll list here the components that need it then start this.

samouss commented 5 years ago

Yep indeed for use case where we don't have a widget implemented but the connector exist (e.g. connectAutocomplete). But even with those cases we can reduce the boilerplate code to extend this class. There are some abstractions that leak a bit: like the BEM root element or even the fact that the user have to manually call the connector. We can even think of another solution than the BaseWidget class, not sure it's the best "Angular" way.

tkrugg commented 5 years ago

Components for which we want to add support for ng-template

- those that are already done are checked
- those that are not relevant have been crossed out.