3
votes

Is it possible to render static HTML fragments without having an associated view-model in Aurelia? For instance, I have a typical header, body, footer layout. In body, I have the router view. There are a set of links in the footer such as FAQs upon clicking which I want to render a view in the body area.

When I try to define a route config for the faq route, the config is expecting one of You must specify a "moduleId:", "redirect:", "navigationStrategy:", or "viewPorts:".

The temporary work around that I have is to create a passthrough view model that doesn't do anything. This is resulting in a bunch of passthrough view model classes. I am sure I am doing something wrong.

I couldn't find any help online with this use-case. Any references will be highly appreciated.

1

1 Answers

2
votes

It seems like you're looking for an inlineView("<your html here/>") type of functionality for routes so that navigating to the target route will directly render the HTML in the router-view element.

This is not directly possible with aurelia-router because without a ViewModel, no ActivationStrategy can be invoked. Aurelia-router wants to call canActivate, activate, canDeactivate, deactivate on something.

However, if you simply want to define markup programmatically, and you don't want to declare a ViewModel for each individual piece of markup, then that can be solved quite neatly with the compose element in combination with inlineViewStrategy.

With this approach, you only need one View/ViewModel pair which is responsible for retrieving the correct HTML based on the current route, and then render that HTML. There are also other ways to do this, but AFAIK this approach involves the least amount of plumbing.

Of course you also need an object to store the HTML/route pairs in, and a service to store/retrieve those objects.

You can see a live working version here (including a few comments to clarify things): https://gist.run/?id=8c7e02ce1ee0e25d966fea33b826fe10

app.js

import { inject } from "aurelia-framework";
import { Router } from "aurelia-router";
import { FaqService } from "./faq-service";

@inject(Router, FaqService)
export class App {
  constructor(router, faqService) {
    router.configure(config => {
      config.map({ route: "", moduleId: "./empty" });
      config.map({ route: "faq/:route", moduleId: "./faq-detail" });
    });
    this.router = router;
    this.faqService = faqService;
  }

  openFaq(item) {
    this.router.navigate(`faq/${item.route}`);
  }
}

app.html

<template>
  <router-view></router-view>
  <ul>
    <li repeat.for="item of faqService.faqItems" click.delegate="openFaq(item)">
      ${item.title}
    </li>
  </ul>
</template>

empty.js (just a convenience for default empty route):

import { inlineView } from "aurelia-framework";
@inlineView("<template>no content</template>")
export class Empty {}

faq-service.js

import { singleton } from "aurelia-framework";

class FaqItem {
  constructor(route, title, markup) {
    this.route = route;
    this.title = title;
    this.markup = markup;
  }
}

@singleton(false)
export class FaqService {
  constructor() {

    this.faqItems = [
        new FaqItem("question-1", "Question 1", "<h4>Question 1</h4><p>Answer 1</p>"),
        new FaqItem("question-2", "Question 2", "<h4>Question 2</h4><p>Answer 2</p>"),
        new FaqItem("question-3", "Question 3", "<h4>Question 3</h4><p>Answer 3</p>")
    ];
  }

  getByRoute(route) {
    return this.faqItems.find(i => i.route === route);
  }
}

faq-detail.js

import { inject, InlineViewStrategy } from "aurelia-framework";
import { FaqService } from "./faq-service";

@inject(FaqService)
export class FaqDetail {
    constructor(service) {
        this.service = service;
    }

    activate(param) {

        let item = this.service.getByRoute(param.route);
        this.viewStrategy = new InlineViewStrategy(`<template>${item.markup}</template>`)
    }
}

faq-detail.html

<template>
    <compose view.bind="viewStrategy"></compose>
</template>