Aurelia How do I use a Child Router and dynamics child routes - parent-child

Trying to use a child route in Aurelia. Can't seem to get my head around the workings of nested routes. Are all routes derived from the root of the app or relative to location of the current router?
Why wont my route-href work in this example? I have a route in this router named screen and it does have an :id parameter
screens/list.ts
#inject(Router)
export class ScreensList {
heading;
router;
screens: any[];
constructor(router){
this.heading = 'Child Router';
this.router = router;
this.screens = [
{
id: 1,
name: 'my screen'
},
{
id: 2,
name: 'my other screen'
}
]
router.configure(config => {
config.map([
// dynamic routes need a href, such as href: screen
{ route: 'screen/:id', moduleId: 'screens/screen/display', name: 'screen', title: 'Screen #1' }
]);
});
}
}
List View
screens/list.html
<li repeat.for="screen of screens">
<a route-href="route: 'screen', params: { id: screen.id }"/>Screen #${screen.id}</a>
</li>
I then have a dummy VM/V at screens/screen/display.
Do I really have to specify the full filepath for a module in a nested child router. I thought it would be routes relative to the location of the parent router or at least the name (root) of the parent?
vendor-bundle.js:11582 ERROR [route-href] Error: A route with name ''screen', params: { id: screen.id }' could not be found.
Check that `name: ''screen', params: { id: screen.id }'` was specified in the route's config.

In your example, you are injecting the router, which is the router configured in app.js, and then calling its configure method. Aurelia is Convention-Over-Configuration. So, use the convention and you will be fine. The configureRouter method will do the tricks for you. For instance:
export class ScreensList {
configureRouter(config, router) {
config.map([
{ route: 'screen/:id', moduleId: 'screens/screen/display', name: 'screen', title: 'Screen #1' }
]);
this.router = router;
}
}
Remember that ScreensList must be a screen of your router. It will not work if it is a custom element.
Take a look at the skeleton-navigation examples https://github.com/aurelia/skeleton-navigation. There are good examples, including child routing.

Related

Vue3 router: How to make an active menu item when on a sub route?

So, it looks I am doing something wrong.
From the docs, if I have a <router-link to="/articles">Articles</router-link> if the current routes are either /articles, /articles/1, /articles/2, articles/2/foo should always get at least router-link-active class... and when it is "root" like /articles it should also get a router-link-exact-active
but mine doesn't... I only get both (router-link-active and router-link-exact-active) when on /articles... but when on /articles/1 (or any other sub route) I get nothing.
this is my router main
const routes = [
{
path: "/",
name: "Landing",
component: LandingPage,
},
{
path: "/articles",
name: "ArticlesList",
component: ArticlesListPage,
},
{
path: "/articles/:id",
name: "ArticleDetails",
component: ArticleDetailsPage,
}, ...
]
const router = createRouter({
history: createWebHistory(),
routes,
});
I would get it right, if the path "/articles/:id" would be a part of "/articles" children, but then I would need a nested router-view, which I don't want

Apply different css files between 2 sub roots

I have an angular app, which consists of a website and system.
so I have made 2 sub roots under app root, websiteMaster, and systemMaster.
CSS files of the website don't have to be loaded when I'm logged in.
CSS files of the systems don't have to be loaded when I'm logged out.
so I need to load CSS files in websiteMaster only for website sub root components and to load CSS files in systemMaster only for system sub root components.
Is there a way to apply that using Angular 8?
Thanks in advance
You've to manually load/unload css files in a main/root(whatever you want to call it) component in one of your subroot module.
Let suppose websiteMaster module has following structure
Routes:
// Wrapping all routes in a parent component
const routes: Routes = [
{ path: '', component: WebsiteMasterRootComponent, children: [
{ path: 'any-path', component: AnyComponent },
{ path: 'any-other-path', component: AnyOtherComponent },
]
}];
WebsiteMasterRootComponent: which will load and unload css files related to this module
export class WebsiteMasterRootComponent implements OnInit, OnDestroy {
// css files required for current module
private styles = [
{ id: 'css-file-1', path: 'assets/css/css-file-1.css' },
{ id: 'css-file-2', path: 'assets/css/css-file-2.css' },
{ id: 'css-file-3', path: 'assets/css/css-file-3.css' },
];
constructor() {}
ngOnInit() {
this.styles.forEach(style => this.loadCss(style));
}
ngOnDestroy() {
// remove css files from DOM when component is getting destroying
this.styles.forEach(style => {
let element = document.getElementById(style.id);
element.parentNode.removeChild(element);
});
}
// append css file to DOM dynamically when current module is loaded
private loadCss(style: any) {
let head = document.getElementsByTagName('head')[0];
let link = document.createElement('link');
link.id = style.id;
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = style.path;
head.appendChild(link);
}}

How to use Aurelia's router with server-side routing

I have an Aurelia project that uses server-side routing with MVC. I am using a layout file that points to a main "app" component, like this:
<div aurelia-app="main" start="app" data-model='#Json.Encode( Model )'></div>
Each of my views contain a reference to the layout file and something like this:
<div aurelia-app="main" start="sample-module" data-model='#Json.Encode( Model )'></div>
My main.js is configured like this:
export function configure(aurelia) {
aurelia.use.standardConfiguration()
aurelia.container.registerInstance('viewModel',
Object.assign({}, JSON.parse(aurelia.host.dataset.model)));
aurelia.start().then(a => {
let start = a.host.attributes.start.value;
a.setRoot(start);
});
}
And I am leveraging Aurelia's router (in app.js) like this:
export class App {
constructor() {
}
configureRouter(config, router) {
config.title = 'Aurelia';
config.options.pushState = true;
config.options.root = '/';
config.map([
{ route: ['', 'Aurelia/Home'], name: 'home', moduleId: 'home', title: 'home', nav: false },
{ route: 'Aurelia/SampleModule', name: 'sample module', moduleId: 'sample-module', title: 'sample module', nav: true },
]);
this.router = router;
}
}
This almost works. If I navigate to SampleModule using the Aurelia navigation link, it loads the module but doesn't hit the server - doesn't even hit the View. If I navigate to Aurelia/SampleModule manually, it loads sample-module twice, including the data from the server. The Aurelia router updates the url exactly as I would expect, so if I navigate and then hit refresh it loads from the server correctly.
I want to be able to use the navigation to change the view without refreshing the entire page, but still leverage my server-side routing and hit the Views and Controllers.
I was able to solve my problem by removing the reference to the app from each of the views:
<div aurelia-app="main" start="sample-module" data-model='#Json.Encode( Model )'></div>
as well as removing the start logic from the layout file and main.js, so now my main.js looks something like this:
export function configure(aurelia) {
aurelia.use.standardConfiguration()
aurelia.container.registerInstance('viewModel',
Object.assign({}, JSON.parse(aurelia.host.dataset.model)));
aurelia.start().then(() => aurelia.setRoot());
}
In the back end, the model has a unique property to each page:
public class AppViewModel
{
public HomeViewModel Home { get; set; }
public SampleModuleViewModel SampleModule { get; set; }
}
From the view model (i.e. sample-module.js), it checks to see if the associated property (AppViewModel.SampleModule) is populated, and if not, it makes an ajax call.

Durandal SPA on iPad goes to browser when navigating

Created ASP.Net SPA using Durandal. Added a forms login page that is hit prior to the SPA. When accessed on an iPad and saved to Home screen it opens like a native app, I can login and still stays in same window when it opens the SPA. BUT as soon as I navigate to another route it opens Safari instead.
Is there something I'm missing or any possible way I can avoid this, it would be nice if I can keep it looking like a native app.
EDIT:
As required logic from shell.js from Durandal viewmodels folder:
define(['plugins/router', 'durandal/app'], function (router, app) {
return {
router: router,
activate: function () {
router.map([
{ route: '', title:'Actions', moduleId: 'viewmodels/actions', nav: true },
{ route: 'welcome', title:'Welcome', moduleId: 'viewmodels/welcome', nav: true },
{ route: 'flickr', moduleId: 'viewmodels/flickr', nav: true },
{ route: 'clients', moduleId: 'viewmodels/clients', nav: true },
{ route: 'client/:id', moduleId: 'viewmodels/client', nav: false }
]).buildNavigationModel();
return router.activate();
},
attached: function () {
$("#logoff").show();
}
};
});
You can simply create a new Application in VS 2012+ using the downloadable Durandal SPA template from asp.net site.
Logic from shell.html:
<ul class="nav" data-bind="foreach: router.navigationModel">
<li data-bind="css: { active: isActive }">
<a data-bind="attr: { href: hash }, html: title"></a>
</li>
</ul>

Durandal Routing - routing to a resource with a particular id?

I'm trying to configure my routing so that navigating to #/my displays the contents for #/folder/62 (or some id stored in a variable) - and navigating to #/public displays the contents for #/folder/1 (same concept).
Additionally, I'd like the application to navigate to one of these routes upon loading, depending on whether or not the user is authenticated. The authentication stuff is done, but once the above routes have been configured, I'd like to activate on one of them.
Here's what I have:
activate: function () {
router.useConvention();
router.handleInvalidRoute = function (route, params) {
//debugger;
logger.logError("Invalid route", route, null, true);
};
router.map([
{ url: 'home', moduleId: 'viewmodels/home', name: 'Home', visible: false },
{ url: 'my', moduleId: 'viewmodels/folder', name: 'My Content', visible: false }, // Should display contents of /folder/2
{ url: 'public', moduleId: 'viewmodels/folder', name: 'Public Content', visible: false }, // Should display contents of /folder/3
{ url: 'set/:id', moduleId: 'viewmodels/set', name: 'Set', visible: false },
{ url: 'folder/:id', moduleId: 'viewmodels/folder', name: 'Folder', visible: false }
]);
if (auth.isAuthenticated)
return router.activate('my'); // should show details page of a particular folder
else {
return router.activate('public'); // should show details page of a particular folder
}
}
app.setRoot as described in https://groups.google.com/forum/#!searchin/durandaljs/app.setRoot/durandaljs/t1hrLfOh1oM/RtCekmY0bDAJ could be used to show different views for authenticated/non-authenticated users.
If only authenticated users should be allowed to see content of "folder/specialID" in the example then you might consider not to expose these via router. Use standard compose functionality in #my to load the specialID view/view model instead.

Resources