How to setup Express for Aurelia Routing? - express

How do I specify a catchall or actually a catch[most] for express so when the user selects refresh on a page that is actually contained in the app bundle, the GET doesn't fail.
home.html
profile
app.js
config.map([
{ route: ['', 'home'], name: 'home', moduleId: 'home' },
{ route: ['profile'], name: 'profile', moduleId: './profile/profile'}
]);
If I click on the profile link, the URL shows localhost://profile and the page renders correctly without performing a GET because the requested resource was bundled in the initial GET. But lets say I refresh the page with localhost://profile, then it makes a server GET request for that page.
If on the server I specify something like:
app.use('/', express.static(__dirname));
app.use('/profile', express.static(__dirname));
It works properly. I was anticipating some type of catch all formatting so I don't have to add every possible route for an app with all routes bundled. Something like:
app.use('/*', express.static(__dirname));
Then the following to capture GET's for another app
app.use('/othercoolapp/*', express.static(__dirname)+'/othercoolapp/');
But it doesn't work...

This issue you're encountering applies to every single page application framework and library with pushState routing enabled.
Above your app.listen or equivalent line of code in the file that bootstraps your Express server, add something like this:
app.get('*', function(request, response){
response.sendFile(path.join(__dirname + '/index.html'));
});
The general idea is that this generic wildcard route will capture all URL requests and send the index.html file. This allows Aurelia to handle its own routing.

Related

Custom PageLayoutComponent

To have specific layout for some pages at our project we create few custom PageLayoutComponent's. Some contfiguration example:
{
// #ts-ignore
path: null,
canActivate: [CmsPageGuard],
component: CartPageLayoutComponent,
data: {
cxRoute: 'cart',
cxContext: {
[ORDER_ENTRIES_CONTEXT]: ActiveCartOrderEntriesContextToken,
},
},
},
All work fine with storefront until you will not try to select specific page at smartedit. As result it not use our custom CartPageLayoutComponent, but will use PageLayoutComponent for rendering.
Probably this is because it's not a normal route navigation. Can somebody from spartacus team suggest how this bug can be fixed?
Probably this is because it's not a normal route navigation
I believe your Route should be recognized normally, there is nothing special in adding a custom Angular Route.
So I guess there is something special about the page or URL of Spartacus Storefront that runs in your SmartEdit.
It's hard to tell the reason of your problem without more debugging.
You said your app works as expected when run differently (locally?), but when used in SmartEdit, then there is a problem. Please identify factors that makes the run SmartEdit different from your (local?) run. And try to isolate them. Guesses from top of my head:
production vs dev mode of the build?
exact URL of the cart page?
any difference in configuration between a local version and deployed one to be used in SmartEdit?
I would also add some debugging code to better know which routes configs are available and which one is used for the current route. For debugging purposes please add the following constructor logic in your AppModule:
export class AppModule {
// on every page change, log:
// - active url
// - the Route object that was matched with the active URL
// - all the Route objects provided to Angular Router
constructor(router: Router) {
router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {
console.log({
activeUrl: router.url,
activeRouteConfig:
router.routerState.snapshot.root.firstChild.routeConfig,
allRoutesConfigs: router.config,
});
}
});
}
}
The pages opened in SmartEdit have the same route of cx-preview (e.g. to open faq page in smartedit, request is https://localhost:4200/electronics-spa/en/USD/cx-preview?cmsTicketId=xxxxx. backend can get page id from cmsTicketId). If you want to change the page layout, you can consider use PageLayoutHandler. Spartacus has some PageLayoutHandlers, e.g.
{
provide: PAGE_LAYOUT_HANDLER,
useExisting: CartPageLayoutHandler,
multi: true,
},

VueJS router alias to particular param

I have a lot of articles in my app, and the URL are written like this in Vue Router: /article/:id.
I have particular articles I want to "pin" and have easier URLs. For example: /pinned-article, which should point to /article/3274 and /other-pinned-article, pointing to /article/68173.
I though about adding this to my routes, but it doesn't work:
{ path: '/article/3274', component: Article, alias: '/pinned-article' }
I thought about something else, involving another component:
{ path: '/pinned-article/:id', component: PinnedArticle }
The component PinnedArticle silently aliasing the correct article with a command like router.alias in the <script> section, but it apparently doesn't exist.
Is there a way to solve this problem? I thought I could use some answers I read here in Stackvoverflow (for examples when it comes to redirect /me to /user/:id, but it doesn't apply.
Thanks in advance :)
addRoute
You can achieve this with Dynamic Routing, which is not the same as dynamic route matching, i.e. route params.
(This solution works in both Vue 3 and Vue 2 with Vue Router >= 3.5.0)
By using the addRoute method of Vue router, you can create routes at runtime. You can either use a redirect or not, depending on whether you want the url bar to read /article/3274 or /pinned.
Redirect
If you want the url to change from /pinned to /article/3274, use redirect:
methods: {
pinRoute() {
this.$router.addRoute({
path: '/pinned',
name: 'pinned',
redirect: { name: 'article', params: { id: 3274 }}
})
}
}
Access the route like:
this.$router.push('/pinned')
The above example assumes you give your Article route a name: 'article' property so you can redirect to it
Alias
You can keep the URL as /pinned using alias. Normally the alias would go on the existing Article route definition, but that doesn't work well with route params. You can use a "reverse alias" with a new route:
methods: {
pinRoute() {
this.$router.addRoute({
path: '/params/3274',
name: 'pinned',
alias: '/pinned',
component: () => import('#/views/Article.vue') // Article component path
})
}
}
Access the route like:
this.$router.push('/pinned')
Notes:
You'll probably want to pass an id argument to the pinRoute methods rather than hardcode them like in the examples above.
A nice thing about addRoute with either method above is if the route already exists, say, from the last time you called the method, it gets overwritten. So you can use the method as many times as you like to keep changing the destination of /pinned. (The docs in both Vue 2 and Vue 3 say the route definition will get overwritten, though Vue 2 router throws a duplicate route warning.)
Of course the pinned route won't automatically persist between app refreshes, so you'll need to save/load the pinned id (i.e. using localStorage, etc.) and run one of these methods on app load if you want that

Vue-Routes redirect doesn't work and beforeEnter render App component again

I'm getting some issues when trying to redirect to an external link.
for ex:
{ path: '*', redirect: 'https://google.com'}
when I use "redirect" it doesn't work completely, but when I use something like that
{ path: '/*',
beforeEnter(to, from, next) {
window.location = "https://google.com"
}
}
it works but there is a problem because first, it tries to render App component again but there is no component so be empty and a blank page is being rendered for nearly 1-1.5 second then it redirects to target URL and I don't want it to reload App component, just redirect it to other link. I googled but found nothing noteworthy.
Or maybe is there another way like deactive a component or use v-if or directly rendering a html file?
redirect is meant to redirect to another route defined by your application, not to go to another website directly.
window.location works, but I think the behavior is somewhat browser-dependent.

How should routing html pages be done for website?

I'm using MEAN stack. For example, if I click on a item, how does it route it's item page like *****.com/item/[id]? How does it go to that item page for that id? Is this done using node and express?
Call the route and send file, somelike this...
app.get('/item/:id', function (req,res){
console.log(req.params.id); be show the param id
res.sendFile('page.html', {id: req.params.id});
)};
More aboute routes
Okay, you need to understand routing mechanism of single page applications. Now you should be aware that your root path that is '/' will be served by express
app.get('/', function(req, res) {
res.sendFile('index.html', { root: path.join(__dirname, '../public') });
});
As soon as your index.html is served to the client, Angular will take over all the routing. You must use a routing solution for that(ui state or ngRouter). if you want to go to a particular route in angular containing some id you can create a route like this(assuming you are using ui-state router)-
$stateProvider
.state('user', {
url: "/user/:id",
templateUrl: "/angular/users/views/user.html",
controller: "UserCtrl"
});
In your html file you will write -
<a ui-sref="user({id: userId})">
Here userId is the id you want to pass to the route.
Hope it helps!

Redirect within MapUnknownRoutes in Aurelia

I want bad routes to navigate to the root route. I've added a mapUnknownRoutes configuration on my router.
config.mapUnknownRoutes((inst) => inst.config.moduleId = 'home');
But this leaves the route untouched. For example, #/fakeRoute routes to home. Ideally, I would like a behavior similar to returning { redirect: '#/' }, which cancels navigation and creates a new navigation to the route '#/'. Is this a feature?
The mapUnknownRoutes method also accepts a RouteConfig object so you can just directly specify your redirect there:
config.mapUnknownRoutes({ redirect: '#/' });
See the complete signature of the method on github