Carlos Cifuentes
AngularJS → Angular reference

require (parent controllers) in modern Angular

require: '^^parent' becomes inject(ParentComponent). Angular looks for the parent up the element tree, the same way require did.

Before · AngularJS
app.component('tabPane', {
  require: { tabs: '^^tabGroup' },
  bindings: { title: '@' },
  transclude: true,
  template: '<div ng-show="$ctrl.active" ng-transclude></div>',
  controller: function () {
    this.$onInit = function () { this.tabs.addPane(this); };
    this.$onDestroy = function () { this.tabs.removePane(this); };
  }
});
After · Angular
@Component({
  selector: 'app-tab-pane',
  template: `<div [hidden]="!active()"><ng-content /></div>`,
})
export class TabPaneComponent {
  private tabs = inject(TabGroupComponent);
  title = input.required<string>();
  active = computed(() => this.tabs.selected() === this);
}

// in TabGroupComponent: the parent finds its panes with a query
panes = contentChildren(TabPaneComponent);
selected = signal<TabPaneComponent | undefined>(undefined);

What changes

  • An optional require ('?^^parent') becomes inject(ParentComponent, { optional: true }).
  • Children no longer register themselves with addPane and removePane: the parent reads them with contentChildren or viewChildren.
  • When the two components are not ancestor and descendant, use a service listed in the providers of their common container.

Migration tip. A parent and a child that reference each other's classes from separate files create a circular import. Keep the pair in one file, or have the child inject an InjectionToken that the parent provides.

Related

Planning a migration or a micro-frontend platform?

In a 20-minute call you describe your situation and I tell you how I'd approach it. If it isn't worth doing yet, I'll say so.

Message me on LinkedIn