How to add dynamic component to Angular Router - javascript

I'm creating pluggable angular app.
I've found the following article:
Building an extensible Dynamic Pluggable Enterprise Application with Angular
Generally, everything works fine, but when I've tried to add angular router then I met some problems.
Currently, I'm not able to add the dynamically loaded component to the router.
I've tried something like this:
this.pluginLoader.load(pluginName).then(moduleFactory => {
const moduleRef = moduleFactory.create(this.injector);
const entryComponent = (moduleFactory.moduleType as any).entry;
const compFactory = moduleRef.componentFactoryResolver.resolveComponentFactory(
entryComponent
);
this.router.config.push({
path: `${pluginName}`,
component: entryComponent
});
this.router.resetConfig(this.router.config);
this.router.navigate([`/${pluginName}`]);
});
But this code cause following error:
core.js:15723 ERROR Error: Uncaught (in promise): Error: No component factory found for function(){this.x=!1}. Did you add it to #NgModule.entryComponents
I've tried also use loadChildren property instead of the component property but I don't know what path should I use.
How can I add the component to the component factory, or how can I find a proper path for such components?

I've found a solution, to add the dynamically loaded module to the angular router we have to use LoadChildren property, but directly from Route object. In this case, we have to create Route object first and then add it to the router config.
loadPlugin(pluginName: string) {
this.pluginLoader.load(pluginName).then(moduleFactory => {
const route: Route = {
path: `${pluginName}`,
loadChildren: () => moduleFactory
};
this.router.config.push(route);
});
}

Related

How to test plain Vue components (not single file components)

All of these tries are throwing an error:
var testUtils=require('#vue/test-utils'), Vue=require('vue');
require('jsdom-global')();
testUtils.mount(Vue.component('test', {
template:'<div>test</div>'
}));
testUtils.mount(Vue.component('test', {
render:function(el) { return el('div', 'test'); }
}));
testUtils.mount({
template:'<div>test</div>'
});
#vue/test-utils/dist/vue-test-utils.js:2471
var componentInstance = node.child;
TypeError: Cannot read property 'child' of undefined
I have also tried to use localVue, to use shallowMount instead of mount and tried to pass Vue.options.components.test after registrating it globally (and some other things that came to my mind) but nothing works.
Isn't there any way to test vue components without using single file components, webpack and/or other technologies that are making things unnecessary complicated and require a build process? Or is this just a lack of documentation?
You need to load the DOM before requiring #vue/test-utils. Change your code to this:
require('jsdom-global')();
var testUtils=require('#vue/test-utils'), Vue=require('vue');
// ...
Likely Jest loads JSDOM in some script before Vue is required, which is why it works there.
There's a simple guide for that in their GitHub repo.
https://github.com/vuejs/vue-test-utils/pull/1373/files#diff-b64ec6abf844db0ffa41aaf83deb3043f880b0beb988a14e0b2ace310848a335
require('jsdom-global')()
const assert = require('assert')
const Vue = require('vue')
const VueTestUtils = require('#vue/test-utils')
const App = Vue.component('app', {
data() {
return {
msg: 'Hello Vue Test Utils'
}
},
template: `
<div>{{ msg }}</div>
`
})
const wrapper = VueTestUtils.shallowMount(App)
assert.strictEqual('Hello Vue Test Utils', wrapper.text())

Vue with dynamic routes and components

Trying to build a test app to see if Vue is a suitable replacment for our AngularJS app. Trying to learn Vue at the same time.
After the user logs in we fetch some roles for that user. Base off those roles is how the menu gets built.
User1 { Role1, Role2, Role3}
In theory
User2 {Role1, Role3}
So Role1 would have a path of /start/page1 and page1 (component) and two child components.
Same with Role2 path of /start/page2 and page2 would have components on it.
I don't really want to build the routes until I know which roles the user has.
I'm using quasar-framework.org and using the menu slide out. Trying to create a menu on the fly. Seems like I need the components to already be imported?
I'm able to build the menu by looping through the roles and setting up a list of menus.
Trying to build the routes on the fly using this.$router.addRoutes(newRoute);
To do that I need the component to already be imported.
The Quasar way is load the components on the fly I guess.
In router.js
function loadPage (component) {
return () => import(`../../pages/${component}.vue`)
}
I can't seem to use that function in a method section.
Is this possible in Vue?
Take a look at vue-router lazy loading documentation and Quasar lazy loading documentation
You can't do it in a method, but if the user permission don't match the route permissions the component is never loaded, which is basically what you want.
Example
const routes = [
{
path: '/some-page-protected',
component: () => import('pages/SomePage'),
meta: {role: 'admin'}
}
]
Or
const SomePage = () => ('pages/SomePage')
const routes = [
{
path: '/some-page-protected',
component: SomePage,
meta: {role: 'admin'}
}
]

How can Vue router get current route path of lazy-loaded modules on page load?

I have a vue app with router set up like:
import index from './components/index.vue';
import http404 from './components/http404.vue';
// module lazy-loading
const panda= () => import(/* webpackChunkName: "group-panda" */ "./components/panda/panda.vue");
// ...
export const appRoute = [
{
path: "",
name: "root",
redirect: '/index'
},
{
path: "/index",
name: "index",
component: index
},
{
path: "/panda",
name: "panda",
component: panda
},
//...
{
path: "**",
name: "http404",
component: http404
}
];
So the panda module is lazy-loaded. However, when I navigate to panda page, a console.log() of this.$route.path in App.vue's mounted() lifecycle only outputs
"/"
instead of
"/panda"
But index page works well, it shows exactly
"/index"
as expected.
So how can Vue router get current path correctly of a lazy-loaded page, when page is initially loaded? Did I miss something?
Edit:
It can, however, catch the correct path after Webpack hot-reloads. It catches "/" on first visit of panda, but after I change something in source code, webpack-dev-server hot-reloads, then it gets "/panda".
So I guess it has something to do with Vue life-cycle.
There is a currentRoute property that worked for me:
this.$router.currentRoute
May be you need to use $route not $router
check here : https://jsfiddle.net/nikleshraut/chyLjpv0/19/
You can also do it by $router this way
https://jsfiddle.net/nikleshraut/chyLjpv0/20/
Use this.$route.path.
Simple and effective.
Hide Header in some components using the current route path.
get current route path using this.$route.path
<navigation v-if="showNavigation"></navigation>
data() {
this.$route.path === '/' ? this.showNavigation = false : this.showNavigation = true
}
If You have similar problem the correct answer is to use router.onReady and then calling your logic concerning path. Below the official Vue router docs:
router.onReady
Signature:
router.onReady(callback, [errorCallback])
This method queues a callback to be called when the router has completed the initial navigation, which means it has resolved all async enter hooks and async components that are associated with the initial route.
This is useful in server-side rendering to ensure consistent output on both the server and the client.
The second argument errorCallback is only supported in 2.4+. It will be called when the initial route resolution runs into an error (e.g. failed to resolve an async component).
Source: https://v3.router.vuejs.org/api/#router-onready
For vue 3 (Composition API)
It can be as simple as route.path if you define the variable route as: const route = useRoute()
Usage example
If you try the following, each time your route path changes it will console log the current path:
<script setup>
import {useRoute} from 'vue-router'
const route = useRoute()
watchEffect(() => console.log(route.path))
</script>

Async components Vue 2

I'm trying to use async components. Here is my configuration:
Vue 2 using Single File Component approach
Webpack 2
Vue Router
The app is pretty basic, I have an "everyone" section contained in App and an "admin" section contained in Admin. I would like to load the component and all the .js related to the Admin if and only if I'm visiting the corresponding route.
After reading the vue-router docs on Lazy Loading, and the one of Vue2 on async components, I'm still not sure how to do that especially with the Single File Component approach.
Here is what I did for the moment but I don't know if it is ok since in the documentation of Vue2 they said :
Vue.component(
'async-webpack-example',
() => import('./my-async-component')
)
Also what do I have to do with webpack so it creates a chunk of everything related to Admin so that adminChunk.jsis just loaded when reaching admin route ?
What is the syntax to make a single file component a async component ?
app.js
const Admin = resolve => {
// require.ensure is Webpack's special syntax for a code-split point.
require.ensure(['./components/admin/Admin.vue'], () => {
resolve(require('./components/admin/Admin.vue'))
})
};
const routes = [
{ path: '/', component: App },
{ path: '/admin', meta: { requiresAdmin: true }, component: Admin},
];
Admin.vue
<template>
<admin-menu></admin-menu>
<child></child>
</template>
<script>
import AdminMenu from './Admin-Menu.vue'
import Child from './child.vue
export default{
data () {
},
components: {
AdminMenu,
Child,
},
}
</script>
You can pass a third parameter to the require.ensure function with the name of the chunk.

Where to define routes in Ember CLI?

I've created some routes using ember generate route {my_route_name} and it creates a js file under routes and a hbs file under templates
Now I want to define these routes like
App.Router.map(function() {
this.resource('posts');
this.resource('post', { path: '/post/:post_id' });
});
But where do I do that in ember-cli?
I've tried adding it in the app.js file right under this code
var App = Ember.Application.extend({
modulePrefix: 'front', // TODO: loaded via config
Resolver: Resolver
});
But that gives me an error: Uncaught TypeError: Cannot read property 'map' of undefined
So I am a little confused as to where to actually define all my routes?
They should be defined in the app/router.js file.
Since you used ember generate route it's likely that a route is already defined there for you, you'll just need to update it.

Categories

Resources