While the default Aurelia convention of a view model looks for a matching HTML file in the same directory is what you want a lot of the time, sometimes you might want to load views from a centralised location like a views directory or ask the server to render you some HTML.
In an application recently some components which have a bit of user-focused data in them populated by the server had to be implemented. I could have duplicated them, but it seemed to be a waste of code when I could render the Razor partials in my Aurelia application instead.
There are two approaches to make Aurelia load views in your app from a different location. You can use ViewLocator and override the prototype method convertOriginToViewUrl
to force all views to be loaded from somewhere else or you can override on a per-model basis.
In my instance I only wanted specific view models to get views elsewhere, not all views. I will detail both ways, honestly there is not a lot to read or know, but the Aurelia documentation is hard to search and confusing, so hopefully this helps.
For all examples outlined below, please note all views need to be wrapped in <template>
tags for the views to be valid. This includes any partials returned by a server controller or API.
ViewLocator.prototype.convertOriginToViewUrl
Overriding this Aurelia method will force all views to be loaded from where you specify outside of the normal convention. If your application is to have all of its views in another folder (like views) or your server is controlling your HTML, you would use this.
The following code goes inside of your main.ts
or main.js
file just before you call aurelia.start
which bootstraps your app. This following code example assumes your views are in a directory called Views
and all view files have a HTML extension.
import {Aurelia, ViewLocator, Origin} from 'aurelia-framework';
export function configure(aurelia: Aurelia) {
aurelia.use
.standardConfiguration();
ViewLocator.prototype.convertOriginToViewUrl = (origin: Origin)=>{
var moduleId: string = origin.moduleId;
var name = moduleId.split('/')[moduleId.split('/').length-1].replace('ViewModel','View').replace('.js','').replace('.ts','');
return `Views/${name}.html`;
}
aurelia.start().then(() => aurelia.setRoot());
}
The caveats to this method like I explained earlier are all of your views will now be loaded from this new directory. If all of your views are in a different location to one another, this method is not going to work for you. You need to be aware this changes how all view files are loaded in your app.
ViewModel Override with @useView or getViewStrategy (per view model basis)
This is a solution for those times when you want only a specific custom element or view model to render a view template from somewhere else. In my use-case, the server was rendering a complex navigation bar with a heap of URL’s with user specific ID’s and values in them. I could have recreated this in Aurelia, but it would have require significant work.
Below I will present to you two solutions. One involves a decorator and the other a view model class method. I personally prefer the decorator, but if you are happy just creating a class method, that will work too.
Using the useView decorator
You can supply a URL or specific location of a HTML file to this decorator. In the below example we are dealing with a theoretical directory called somelocation
and a file called my-view.html
.
import {useView} from 'aurelia-framework';
@useView('somelocation/my-view.html')
export class MyViewModel {
}
Using the class method getViewStrategy
This does the same thing as the decorator, the only difference is you do not need to import anything and some people might honestly prefer that.
export class MyViewModel {
getViewStrategy() {
return 'somelocation/my-view.html';
}
}
As you can see we are supplying the same location, the function here just expects a string to a template or URL to somewhere HTML is rendered to use as the layout.
Conclusion
Simple. In the above examples if you are asking a .NET controller to render a Razor HTML partial, simply supply it the URL to the controller and action that renders the HTML and Aurelia will use this as your view. It does not get any more simple than that.
Hi
When i add this my MVC controller action gets hit – but Aurellia throws an exception when rendering the template.
homec.ts =>
import {useView} from ‘aurelia-framework’;
@useView(‘home/index’)
export class homec {
}
I have a homec.html, that just contains an empty definition =>
Any ideas?
I guess an even better (ie. more scalable) way, would be to create a custom decorator which dynamically inserts a `getViewStrategy` method into the class. I guess this is what `@useView` does, but is limited to a file location.
`@serverView(‘views’)`
This _almost_ works for me.
Aurelia certainly attempts to load the partial from my MVC controller. Problem is, then it throws this little doozy of an error –
“Template markup must be wrapped in a element”
And it’s game over. Has something changed in the 14 months since you wrote this? That wouldn’t be surprising.
Scratch that! The problem was ViewStart.cshtml specifying the default layout – obviously, I’m using custom layouts anyway – so once I removed
@{
Layout = “~/Views/Shared/_Layout.cshtml”;
}
and left it as a blank file, everything was fine.