Aurelia 2 - custom `ViewStrategy` -> `@useRemoteView`

Hi guys,

I looking into aurelia@2.0.0-beta.2 project that is using @aurelia/router-lite. Based on getting started examples, I’ve noticed that components now require:

import { customElement } from '@aurelia/runtime-html';
import template from './home.html';

@customElement({ name: 'ho-me', template })
export class Home {
  private readonly message: string = 'Welcome to Aurelia2 router-lite!';
}

HTML template has to be imported and explicitly assigned to the @customElement decorator.

Is there a way to customize ViewStrategy as it was the case in Aurelia 1?

For certain custom elements, I would use custom @useRemoteView decorator, which relies on custom ViewStrategy (RemoteViewStrategy), to fetch template HTML from server:

@useRemoteView("/template/home")
export class Home {
}

Hi @nenadvicentic! The process of creating a custom element definition is completely synchronous ATM. However, you can create your own infra to fetch the template from a remote source, and then use the CustomElement.define API to create the custom element definition, and thereafter register to the DI, if neeeded.

1 Like

Hi @Sayan751 ,

Thank you the for the hint. However, this is lowest-level approach, that would require from me (framework user) to know Aurelia v2 upside down.

There are way too many properties that I have no idea what to do with (capture, bindables, needsCompile, isStrictBinding, shadowOptions, etc…).

Simple example breaks immediatelly:

import { CustomElement } from '@aurelia/runtime-html';

async function defineElement(){
  const response = await fetch("/template/home");
  const template = await response.text();

  CustomElement.define({ name: 'ho-me', template });
}

defineElement();

With an error:

Uncaught Error: Invalid route config property: “.routes[0].component”. Expected function, object or string (see Routeable), but got undefined.

On top of that, even if this would work, this code is extremely unmaintainable, since it’s completely differs from regular code of Aurelia. And I did not even try to add component properties and functions yet.

Is there any plan to implement something similar to ViewStrategy from Aurelia v1, that is easily customizable by end-user?

This is on the contrary is rather simple and straightforward.

import { Aurelia, StandardConfiguration } from '@aurelia/runtime-html';
import { bindable, Class, CustomElement } from 'aurelia';

const MyAppTemplate = '<foo-bar msg.bind></foo-bar>';
class MyApp {
  private readonly msg = 'Hello Aurelia2!';
}

const FooBarTemplate = '<b>${msg}</b>';
class FooBar {
  @bindable public msg: string;
}

async function getTemplate(id: string) {
  // immitate server delay
  await new Promise((res) => setTimeout(res, Math.random() * 2000));
  switch (id) {
    case 'foo-bar':
      return FooBarTemplate;
    case 'my-app':
      return MyAppTemplate;
    default:
      throw new Error(`unknown component id: ${id}`);
  }
}

async function createComponent(name: string, type: Class<unknown>) {
  return CustomElement.define(
    { name, template: await getTemplate(name) },
    type
  );
}

(async function () {
  const host = document.querySelector<HTMLElement>('app');
  const au = new Aurelia();
  au.register(StandardConfiguration);

  const [component, foobar] = await Promise.all([
    createComponent('my-app', MyApp),
    createComponent('foo-bar', FooBar),
  ]);

  au.register(foobar);
  au.app({ host, component });
  await au.start();
})().catch(console.error);

You can also check out the live example: discourse 5106 - StackBlitz

3 Likes

@Sayan751 Your example worked well for custom element (e.g. <foo-bar>) that has to be registered so it can be used all over the application.

I simplified it for my use case, because I wanted to register IRoutableComponent for a route.

import { CustomElement } from "aurelia";
import { routes } from "@aurelia/router";
import { HomePage } from "./home-page";
import { PersonPage } from "./person-page";


@routes([
  {
    path: '',
    component: HomePage,
    title: 'Home'
  },
  {
    path: 'person',
    component: async () => {
      const result = await fetch('/template/person-page');
      const template = await result.text();
      return CustomElement.define({
        name: 'person-page',
        template: template
      }, PersonPage)
    },
    title: 'Person'
  }
])
export class MyApp {
  public message = 'Hello World!';
}
1 Like