Skip to content

Portals

Portals might have different meaning depending on the use-cases. In general, it means that we want to render something as children of something else without following the hierarchy of the template. Pseudo-code looks something like this:

<ngt-group>
<!-- render ngt-mesh here -->
</ngt-group>
<!-- outside of the hierarchy -->
<ngt-mesh></ngt-mesh>

NgTemplateOutlet

For many cases, we can use NgTemplateOutlet if we just want to portal objects around with (or without) different context data. In other words, we can use this technique to reuse templates.

What we’re seeing here is:

  • An Object3D that is being moved back and forth
  • A Mesh as a child of the Object3D
  • A Group
  • Another Mesh as a child of the Group

The main takeaway here is that this Mesh is being reused and has different color based on where it’s rendered.

<ngt-object3D>
<ng-container [ngTemplateOutlet]="forObject3D" />
</ngt-object3D>
<ng-template #forObject3D>
<ng-container [ngTemplateOutlet]="mesh" [ngTemplateOutletContext]="{ color: '#fe3d00' }" />
<ngt-group [position]="[0, 1, 0]">
<ng-container [ngTemplateOutlet]="mesh" [ngTemplateOutletContext]="{ color: '#2f7dc6' }" />
</ngt-group>
</ng-template>
<ng-template #mesh let-color="color">
<ngt-mesh>
<ngt-box-geometry />
<ngt-mesh-standard-material [color]="color" />
</ngt-mesh>
</ng-template>

NgtParent

This technique is useful for when you cannot control the template for, well, ng-template. For example, routed components via ngt-router-outlet

NgtParent is a structural directive and it takes an input parent. parent accepts

  • A string: which will be used to look up the object with getObjectByName()
  • An Object3D
  • An ElementRef<Object3D>
  • or a Signal of all of these above

Attaching *parent on an element will portal that element as a child to the parent input.

NgtPortal

In THREE.js, there is a construct called WebGLRenderTarget. It is used to render the scene into a texture and then render the texture into the canvas. This is useful for things like post-processing effects, or HUD-like visuals.

In Angular Three, we can use NgtPortal component to create an off-screen buffer that can be used to render secondary scenes.

NgtPortal provides a layered NgtSignalStore<NgtState> that its children can inject. This makes sure that children of NgtPortal access the state of the NgtPortal and not the root NgtSignalStore<NgtState>.

@Component({
template: `
<ngt-mesh>
<ngt-torus-geometry />
</ngt-mesh>
<ngt-portal [container]="secondaryScene">
<ng-template portalContent>
<ngts-perspective-camera [options]="{ makeDefault: true }" />
<ngt-mesh>
<ngt-box-geometry />
</ngt-mesh>
</ng-template>
</ngt-portal>
`,
imports: [NgtPortal, NgtPortalContent],
})
export class HUD {
secondaryScene = new Scene();
}

The portal can have its own scene, camera, and children.