Specific Module routes in Jetbrains Compose with Decompose on kotlin desktop application - kotlin

I'm using JetBrains Compose framework on a desktop application project, and for routing they suggest in the official documentation arkivanov-Decompose library for routing between views (Composables).
Works like a charm, but the more views you have, the longer your routing file gets. I was wondering if I could make it look a little bit better.
I'm only familiar with web routing like in Angular, when we can define the routes inside the modules. There, every module can have a module-routes.ts file with something like:
const routes: Routes = [
{ path: 'first-component', component: FirstComponent },
{ path: 'second-component', component: SecondComponent },
];
This way I can manage all elements that concern the module inside the module, and the routes are imported into a global Router module.
In Decompose I'm trying to do something along those lines, so I can encapsulate certain views in their respective modules (some views only interact with views of the same module, but I'm having a hard time having my router to be distributed between the modules. Does anyone have an idea on how to do it?
I have my router and my child set:
private val router =
router<Configuration, Content>(
initialConfiguration = Configuration.Auth, // Starting with Login
childFactory = ::createChild // The Router calls this function, providing the child Configuration and ComponentContext
)
and my child factory:
private fun createChild(configuration: Configuration, context: ComponentContext): Content =
when (configuration) {
is Configuration.Auth -> auth(configuration)
is Configuration.UserList -> userList()
is Configuration.NewUser-> newUser()
can I get those configs from the modules to make it cleaner?
Can I have different routes for different types of user (admin, normalUser, etc...)?

Can I get those configs from the modules to make it cleaner?
Yes, you can move the Configuration sealed class and all its subclasses into a separate file, and make it internal. You can also move the createChild function.
Can I have different routes for different types of user (admin, normalUser, etc...)?
You can define separate configuration classes for each route.
PS: also please note that you can organise your components into a tree. So on each component you will have just a few routes and limited responsibility.
Something like this:
Root
/ \
Auth Main
/ \ / \
SignIn SignUp UserList UserProfile

Related

Nuxt choosing layout before initialize

I use nuxt-generate and nuxt/device package. which provides the device type but for the layout since I use nuxt-generate at first load app uses default layout. not the one provided by nuxt/device. but after i go another page it starts using correct layout. so the problem is how can i have the app use layout at first initialize. or at least change it after initialize. is there any way to do these?
this is how i try to choose layout but never works at first open.
layout (context) {
if(context.isMobile) {
return 'mobile'
} else if (context.isDesktop) {
return 'default'
}
},
when you use nuxt-generate nuxt generates a series of static html files, I don't think there is an easy way to solve this, but there can be a series of workarounds:
use nuxt in ssr mode, so that you have a way of knowing who is calling that particular resource
make a distinction at the level of routes like (/desktop/*** and /mobile/***) where both routes lead to a different layout and in the webserver configuration do a redirect on the correct route
do a middleware that redirects to the correct route /desktop /mobile, but I don't recommend it because you lose the advantage of static generation of nuxt

load routes via component from external api and add them to the router

I would like to load my routes from an external API. Some users might not have the permissions to access a module.
So my navbar makes an API call and gets all the modules returned. These module objects contain the path to the view file.
I tried to create a small sandbox to reproduce the problem
https://codesandbox.io/s/vue-routing-example-i5z1h
If you open this url in your browser
https://i5z1h.codesandbox.io/#/First
you will first get the following error
Url /First not found
but after clicking on the First module link in the navbar, the First view should get rendered.
I think the problem is related to the fact that the page has not yet started the navigation created event after loading and the module page is therefore not found. After changing a router URL the navigation component had enough time to add all the required routes to the router.
How can I load these URLs before the router leads to the first route and responds a 404 error?
The key idea here is to load the routes asynchronously which means you must defer loading of your SPA till that time. In your index.js or main.js, your code would be something like this:
// Some functions are assumed and not defined in the below code.
import Vue from 'vue';
import VueRouter from 'vue-router';
// Application root component
import App from './App.vue';
import { getRoutes } from './api';
// Register Vue plugins
Vue.use(VueRouter);
// Make API call here
// Some animation before the app is fully rendered.
showLoader();
getRoutes(/* Optional User data */)
.then((routesData) => {
// Stop the animation
stopLoader();
return routesData;
})
.then((routesData) => {
// processRoutes returns an array of `RouteConfig`
const routes = processRoutes(routesData);
const router = new Router({
routes: [
...routes,
{
path: '*',
component: NotFound
}
]
});
})
.then((router) => {
const app = new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
});
});
Additionally, there are a few things you need to do:
Routing is generally the higher-level concern. So if you consider DIP - Dependency Inversion and the stateful + singleton nature of the router, then it makes sense to bootstrap it at the very beginning. Thus, anything that router needs should be available. This means that the navbar component should not be responsible for making the API call. You must take it out.
Another possible solution is to use $router.addRoutes() method. But it is inadequate for your needs. It will not work considering authorization in mind. It will not prevent navigation.
On a philosophical level, when you are using SPA with client-side routing, then client-side routing is its own source of truth. It is reasonable to know all the routes upfront and hence most routers are designed with this idea in mind. Thus, a requirement like this is a poor fit for this paradigm. If you need something like this, then a server should possess the knowledge of client-side routes and during page refresh, the server should decide what to do - Load the SPA or reject with 404/403 page. And if the access is allowed, the server should inject routing data in the HTML page which will then be picked by Vue.js on the browser side. Many sophisticated SSR - Server-Side Rendering techniques exist to achieve this.
Alternative strategy: Use guards
Define all the routes upfront in your router for all the possible views of all the users.
Define guards for each authorized routes. All these guards would be resolved asynchronously.
Instead of loading routing data from API, use the API to return an Authorization Matrix. Use this API response in your route guards to determine the access.
To prevent calls to the same API multiple times, you can use some sort of caching like Proxy, Memoization, store etc. Generally, for a user, the Auth Matrix will not vary between the calls.
As an advantage of this, you can still load the application partially if required leading to meaningful user experience by reducing the user's time to interact with the application.

Large Scale App: Multiple Vue apps or one Vue app with multiple components?

I am beginning a project where I am tasked with combining 7 codebases into 1 codebase. Each of these codebases are very different and handle different functions (Registration, Surveys, Email Blaster, etc). I would like to use Vue for the frontend of each of these (Laravel on the backend).
My initial structure for this project involves using one app.js file with one Vue instance and separating each of the codebases into components. Vue-Router will determine which one of the components gets loaded.
//example of the layout thusfar
let routes = [
{ path: '/registration', component: require('./components/admin/Registration.vue').default },
{ path: '/surveys', component: require('./components/admin/Surveys.vue').default }
]
Is this the best way to layout my project? If not, is there a better way to structure a project like this?

path-to-regexp Find regular expression matching the route

I am adding dynamic child components while the page loads. Adding child routes in OnCreated of parent does not make the page work when we refresh the page.
Hence, I am parsing the page templates (as I know them when the page loads).
I am now looking for a way to know the route which matches the href. Vue-js uses path-to-regexp and my question is very simple.
I want to know the matching component
const router = new VueRouter({
routes: [
// dynamic segments start with a colon
{ path: '/user/:id', component: User },
{ path: '/foo/bar', component: FooBar },
]
})
// Reverse of this
var matchingComponent = howDoIDothis(/foo/bar) // this should give me the matching
I need this so that I can remove from the path and then add the child component to the parent dynamically.
You need to use Vue router's router.getMatchedComponents method. However, this method needs that your router is fully initialized with all the routes. Otherwise, there is no way. Vue-router doesn't expose underlying parsed Regular expressions against which you can compare your href.
Also, what you are trying to do is not the most idiomatic way of doing things in Single Page Applications. A good practice is to declare all your routes upfront in some JS file using which you should initialize your router. Of course, you will want to protect certain routes for which you should use Route guards.
Finally, when you have all your routes declared upfront means you have all the components bundled upfront in one big JS file. To avoid this, wrap your component in async wrappers and bundler like Webpack would be smart enough to split the bundle into multiple smaller files.

Vuejs have multiple components sharing variables and functions

Right this is more about practice, I haven't really got a working scenario but I can provide an example. Imagine Facebook messenger, you have a chat widget in the header where you can quickly view and respond to messages. You can also click view all to take you off to the messages page.
So to me I can see myself having two Vue components, they're more sibling than parent and child as one would be used on all pages and the other simply on the messages page.
<chat></chat> and <chat-widget></chat-widget>
Now from what I can see, across the widget and the chat window itself is functions that would operate in the same way, maybe they'll have slightly different templates because of where they are loaded but somethings straight of the bat would be:
messages() {}
compose() {}
validate() {}
These are just some examples as I use Laravel as my backend I would be using axios to send requests between my Vue frontend and my backend (database).
Ideally, I wouldn't want these components to duplicate it's functions, so should I simply duplicate them or is there a way where i can store some sort of parent functions?
One problem is because of async ajax requests I can't simply call a function that returns say the messages for me to bind, at least, I don't think I can.
Just looking for some guidance on how I can best do this with Vue so that I'm not duplicating identical functionality within my components.
You can use composition to create a "base/abstract" component from which other components can extend:
https://v2.vuejs.org/v2/api/#extends
var CompA = { ... }
// extend CompA without having to call `Vue.extend` on either
var CompB = {
extends: CompA,
...
}
You can inherit the functionality from the "base/abstract" class or override parts of the functionality of the parent.
Another thing you could do is to create mixins:
https://v2.vuejs.org/v2/guide/mixins.html#ad
This is helpful if your components need to share functionality but are not largely the same. If you were to use extend in those cases you would likely override the majority of base functionality to these are a better in that case.
Of course nothing would stop you from simply extracting commonly used methods into a separate file and importing them in your components directly.