I have my angular project structure as below:
/(root or repo folder)
|_ projects
|_ mylib (this is the main library that will be exported from the repo)
|_ sample-app (created to consume 'mylib ' project to test that things work fine when 'mylib' would be consumed in other projects)
To handle application state I am using ngRx (of which I have only basic knowledge). The project is setup with Angular8.2.5, ngRx 8.3.0 and RxJs 6.5.3.
On doing npm start on the repo, sample-app project is bootstrapped and mylib project is lazily loaded.
Here is how I have initialized the app store/state
In sample-app/app.module.ts (inside sample-app project)
StoreModule.forRoot({}, {
runtimeChecks: {
strictStateImmutability: true,
strictActionImmutability: true,
strictStateSerializability: true,
strictActionSerializability: true,
},
}),
!environment.production ? StoreDevtoolsModule.instrument({ name: 'My Sample App' }) : [],
In mylib/mylib.module.ts (inside mylib project)
import { libReducer} from './shared/store/lib.store';
StoreModule.forFeature('libState', libReducer)
The libReducer is exported from mylib/shared/store/lib.store.ts file
export interface subFeatureOneState {
// some properties defined here
}
export interface LibState {
subFeatureOneReducer: subFeatureOneState;
}
export const libReducer: ActionReducerMap<LibState> = {
subFeatureOneReducer,
};
The only issue I am getting with this setup is that I get an error when I try to build my project( using ng build).
The error says
Checking the logs doesn't provide much help either.
The build issue gets resolved if I change StoreModule.forFeature definition in mylib.module.ts to below
StoreModule.forFeature('libState', subFeatureOneReducer)
But this is not what I desire as I intend to keep all my reducers at one place and just have one reference of StoreModule.forFeature inside mylib project.
I couldn't find much articles online that explain usage of ActionReducerMap for a feature module store. I followed the approach mentioned below, but it didn't solved the build failure issue:
#ngrx/store combine multiple reducers from feature module
Is there something wrong with the way I have configured store/reducers to be initialized? It would be great if I can get any pointers on this to solve the build error issue.
Here is my code I think you are missing the ROOT_REDUCERS
export const ROOT_REDUCERS = new InjectionToken<
ActionReducerMap<State, Action>
>("Root reducers token", {
factory: () => ({
router: fromRouter.routerReducer
})
});
StoreModule.forRoot(ROOT_REDUCERS, {
metaReducers,
runtimeChecks: {
strictStateImmutability: true,
strictActionImmutability: true
}
}),
Here is my full code for your reference
Related
The github repositories mentioned in https://www.npmjs.com/package/idle-vue-3/v/1.0.1 do not have issues page. So, I guess I have to post it here. The documentation provided is not really Vue 3 at all. Many of the APIs used in the documentation are obsolete. Things like import Vue from 'vue';, Vue.use, etc. WTF is that project owner doing!?!
I bump into error with the following code snippet:
app.use(IdleVue, {
eventEmitter: Vue,
//store,
store: Store,
idleTime: idleTimeInMillis,
startAtIdle: false
});
Store is imported from another .js file:
export default async () => {
const Store = createStore({
modules: {
rules,
utils
},
// enable strict mode (adds overhead!)
// for dev mode only
state: {
clients: []
},
mutations: vuexfireMutations,
actions: {
bindClient: firestoreAction(async (context, ref) => {
return await context.bindFirestoreRef("clients", ref);
})
},
strict: process.env.DEV
});
return Store;
}
The browser console shows the following error:
boot error: TypeError: r.registerModule is not a function
which happens on line app.use(IdleVue, {. Any advice and insight is appreciated
What do you mean obsolete? It's scheduled to be deprecated but it's not deprecated yet.
WTF is that project owner doing!?!
The project owner did what he wanted, create an idle-js wrapper for Vue.
Vue 3 was officially release in September 2020.
idle-vue existed even back in 2019.
If you checked the repo, you'd see it was a fork.
The forked repo is released under the name idle-vue: https://www.npmjs.com/package/idle-vue
idle-vue-2 is taken but empty: https://www.npmjs.com/package/idle-vue-2
idle-vue-3 is the fork version with more changes applied to it.
Check the issue: https://github.com/soixantecircuits/idle-vue/issues/49 for your question.
Just use idle-js yourself or make a fork of your own and support Vue 3.
First off, I'm a beginner with NuxtJS and front-end development in general, so it might be that I'm missing something - though I do believe I went through all the options before posting here. Apologies in advance if that is not the case.
I've been having trouble using installed modules that I've registered as plugins. For example, take mapbox-sdk.
After installing it with npm install #mapbox/mapbox-sdk, which correctly creates #mapbox/mapbox-sdk in node_modules, I register it in nuxt.config.js:
plugins: [
...
"~/plugins/mapbox-sdk.js",
],
Of course, I also create the mapbox-sdk.js file in plugins/, containing:
import "#mapbox/mapbox-sdk";
Then, in a page (say, myMap.vue), when I try:
var mapboxClient = mapboxSdk({ accessToken: MY_ACCESS_TOKEN });
which is the basic usage example in the documentation, I get:
mapboxSdk is not defined
in the console. This behavior extends to every single module I installed today, but is not the case for modules I had previously installed.
The reason why you're getting the error mapboxSdk is not defined is because there are a few issues with the way you've set up this plugin.
Docs here https://nuxtjs.org/docs/2.x/directory-structure/plugins/, they have some useful diagrams.
There are a couple of ways you can use this package.
Plugin
// ~/plugins/mapbox-sdk.js
import mapboxSdk from '#mapbox/mapbox-sdk'
export default (_ctx, inject) => {
// Exposing the mapboxSdk to your Nuxt app as $mapBox.
inject('mapBox', mapboxSdk)
}
Then in nuxt.config.js, same as you've already done.
plugins: [
...
"~/plugins/mapbox-sdk.js",
],
Then in your component myMap.vue
var mapboxClient = this.$mapBox({ accessToken: MY_ACCESS_TOKEN });
Directly in the component:
If you don't wish to use a plugin, the way that #kissu mentioned above https://stackoverflow.com/a/67421094/12205549 will also work.
Try adding this after the import to let Vue know that this method exists (in the same .vue file) at first
<script>
import mapboxSdk from '#mapbox/mapbox-sdk'
export default {
methods: {
mapboxSdk,
},
mounted() {
console.log('mapbox function >>', mapboxSdk)
},
}
</script>
Do you have it working in a .vue component at first ?
I have a Vue.js 2 project with Typescript. In the main.ts file, I've declared 2 variables, that I've wanted to access globally in my project:
// ...
Vue.prototype.$http = http; // this is the library imported from another file, contains various methods such as `get`, `post` etc.
Vue.prototype.$urls = urls; // this is JSON object, also imported from another file
new Vue({
store,
render: (h) => h(App),
}).$mount('#app');
In one of my components, let's call it User I have following mounted code block:
mounted(): void {
this.$http.get(`${this.$urls.getUser}/${this.userId}`);
}
Everything works fine when I'm running a local server (via npm run serve command), but when I create an app build (via npm run build command) and enter the app on the server (or the index.html file on my hdd) I receive following error:
TypeError: Cannot read property 'get' of undefined
at VueComponent.value (user.ts:62) // <-- this line is the one with $http.get from `mounted` hook
I'm not sure how to proceed with this, I've blindly tried to add those global values to various places e.g. in http.d.ts file I have the following:
import { KeyableInterface } from '#/interfaces/HelperInterfaces';
import Vue from 'vue';
declare module 'vue/types/vue' {
interface VueConstructor {
$http: KeyableInterface;
}
}
declare module 'vue/types/vue' {
interface Vue {
$http: KeyableInterface;
}
}
declare module 'vue/types/options' {
interface ComponentOptions<V extends Vue> {
http?: KeyableInterface
}
}
(I've also created urls.d.ts with similar code)
UPDATE #1:
I've tried also following approach - in my main.ts file:
const helperModules = {
/* eslint-disable-next-line #typescript-eslint/no-explicit-any */
install: (vueInstance: any) => {
vueInstance.prototype.$http = http;
vueInstance.prototype.$urls = urls;
},
};
Vue.use(helperModules);
But it still doesn't work (same error).
UPDATE #2:
I've also imported http utility into my user component, and added following console.log to existing mounted callback:
console.log(http, this.$http)
And while working on my localhost, it returns me twice the same value, but when I create a build it returns me:
ModuleĀ {__esModule: true, Symbol(Symbol.toStringTag): "Module"}, undefined
Similar thing happens, when I add console.log(urls, this.$urls) - imported module is being logged, while prototyped one returns undefined.
Any thoughts? Will appreciate any help.
Finally I've overcome the problem by moving the prototyping parts from main.ts to App.ts file.
I'm not 100% sure if it's a valid "the Vue.js way" of solving this, as I've always declared that in main.js file - but I was using then JavaScript & it was "just working" as expected.
Following is the entry point to my library, it generates a component with a dynamic tag:
// muvement.js
import { defineComponent, ref, onMounted, h } from 'vue';
const createMuvement = (tag) => {
return defineComponent({
name: `m-${tag}`,
setup(props, context) {
const root = ref(null);
onMounted(() => {
console.log(root.value);
});
return () => h(tag, { ...context.attrs, ref: root }, context.slots);
}
});
};
const muvement = (...tags) => {
const components = {};
tags.map((tag) => (components[`m-${tag}`] = createMuvement(tag)));
return components;
};
export { muvement };
It's expected to be consumed like so:
// Home.vue
<template>
<div>
<m-div>div</m-div>
<m-button>button</m-button>
</div>
</template>
<script>
import { muvement } from "muvement";
export default {
name: "Home",
components: {
...muvement("div", "button")
}
};
</script>
This works as expected when the library code is contained within the Vue app folder (assuming we are now importing from "#/components/muvement.js" instead of "movement").
That is:
-muvement-test-project (scaffolded with vue-cli)
- src
- views
- Home.vue
- components
- muvement.js
I've also published an alpha release that works fine when importing "muvement" after installing it directly from the npm registry (that is, npm install muvement instead of npm link muvement).
The Problem
During development, I want an app to test the library with that is separate from the library's directory.
I've used npm link to link the library to the test app (as I have done with many other projects in the past).
From /path/to/library
$ npm link
From /path/to/test/app
$ npm link muvement
So far so good. The module is available as a symlink in the test app's node_modules folder. So I import { muvement } from "muvement", run npm run serve, and... BOOM.
Everything explodes (see errors below). It's also probably worth noting that trying to import from the full path (i.e. C:/dev/npm/muvment/dist/es/index.js) results in the same issues as npm link does, so I don't think it has anything to do with the symlink directly.
This is what appears in the console:
For pretty much the entire day I have been trying to solve this one issue. I've seen several seemingly similar questions that were solved by settings Webpack's resolve.symlinks to false but that has no effect on my problem. I've read all through the docs and even Vue's source code (here is the offending line for those who are curious).
Since the warning suggests that the error is commonly attributed to async setup I thought maybe webpack was doing something weird that would make my code async. This doesn't seem to be the case as the call stack of both the working attempt and failed attempt are identical.
What's not identical is the scope.
Here is the scope for the example that is working:
And here is the failing one:
(Notice that the target parameter is null during the call to injectHook, which is obviously what prompts Vue to show a warning).
My question is, why does the location of the imported module make such a difference during the execution of the said module?
The library code and build setup are available here:
https://github.com/justintaddei/muvement
The test app is available here:
https://github.com/justintaddei/muvement/tree/example
If I've left out something important, please let me know in the comments. It's been a long day so I'm sure I've probably missed something.
Thank you.
The problem is your app is using two different vue dependencies under the hood - vue requires the same dependency to be used to keep track on reactivity, lifecycle, etc.
When you link a library npm/yarn will use that linked folder node_modules, but your app is using it's dependencies from it's node_modules.
When your app imports vue it will go app/node_modules/vue but when you import from your linked dependency it will be going to linked_dep/node_modules/vue.
app
node_modules
vue
linked library
node_modules
vue
One easy way to debug this issue is to change both vue dependency files with a console.log and check if the console is logging both.
I have a backend built with Elixir / Phoenix and a frontend built with Angular 2 (Typescript, Brunch,io for building, ES6). I now want to use Phoenix Channels. And I'm a bit desperate trying to use the Phoenix Javascript Client in my frontend.
When I install https://www.npmjs.com/package/phoenix-js via npm install phoenix-js and then try to inject it into a service in angular like this:
import { Socket } from "phoenix-js";
I always get the error Cannot find module phoenix-js during compilation.
I'm a bit stuck and every hint on how to get this to work would be greatly appreciated.
Thanks
Edit: I'm going to leave the old answer below - even though it is a bit emberassing. Getting everything to work and using the most recent version of the Phoenix JS Client with Angular 2 was even easier than I thought and I was just terribly confused.
The Phoenix JS client has been extracted as an npm package and can be found here. Install it with npm install --save phoenix. Then load it as additional dependency. In my setup with SystemJS it was just a matter of adding the necessary configuration:
import { join } from 'path';
import { SeedConfig } from './seed.config';
import { InjectableDependency } from './seed.config.interfaces';
export class ProjectConfig extends SeedConfig {
PROJECT_TASKS_DIR = join(process.cwd(), this.TOOLS_DIR, 'tasks', 'project');
constructor() {
super();
// this.APP_TITLE = 'Put name of your app here';
let additional_deps: InjectableDependency[] = [
// {src: 'jquery/dist/jquery.min.js', inject: 'libs'},
// {src: 'lodash/lodash.min.js', inject: 'libs'},
{src: 'phoenix/priv/static/phoenix.js', inject: 'libs'}
];
const seedDependencies = this.NPM_DEPENDENCIES;
this.NPM_DEPENDENCIES = seedDependencies.concat(additional_deps);
}
}
Now we have it in the global scope and just need to use declare var in the Angular 2 service typescript file where we want to use it. Here was where I made a crucial mistake. I tried to access Socket directly and therefor used declare var Socket: any. Which always led to the error Socket is undefined. But this issue got me in the right direction: If you use the transpiled ES5 version (and not ES6) you have to use Phoenix.Socket (because of namespacing I guess).
So this is how my service looks like now:
import { Injectable } from '#angular/core';
declare var Phoenix: any;
#Injectable()
export class PhoenixChannelService {
socket: any;
constructor() {
this.socket = new Phoenix.Socket("ws://localhost:4000/socket", {
logger: ((kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }),
transport: WebSocket
});
this.socket.connect();
}
}
Now everything works like a charm.
If you don't want to install the client via npm, there is a more basic way: Just get the latest version of the JS Client from GitHub from /priv/static, store it in the folder where you keep your static assets and include it directly in your index.html:
<script src="/path/to/static/js/phoenix.js"></script>
The rest stays the same.
Note: If you want to use it with typescript type definitions, this npm package might be a good starting point - even though it is a bit old.
Old and embarrassing answer: So I think I figured it out. Writing my own definition file wasn't an option. And since all the documented code on how to use the phoenix client is in ES6 I got stuck including the transpiled ES5 version directly in my index.html. But the first clue was this article.
I then found this issue on GitHub which is about extracting the Phoenix Client. Via this I then found this npm package, which is a bit outdated but seems to work. I install it with npm insall --save phoenix-js and then load the dependency in my project. Since my Angular App is based on this seed it goes into my project definition (and make sure to load the Globals version of the phoenix client:
import { join } from 'path';
import { SeedConfig } from './seed.config';
import { InjectableDependency } from './seed.config.interfaces';
export class ProjectConfig extends SeedConfig {
PROJECT_TASKS_DIR = join(process.cwd(), this.TOOLS_DIR, 'tasks', 'project');
constructor() {
super();
// this.APP_TITLE = 'Put name of your app here';
let additional_deps: InjectableDependency[] = [
// {src: 'jquery/dist/jquery.min.js', inject: 'libs'},
// {src: 'lodash/lodash.min.js', inject: 'libs'},
{src: 'phoenix-js/dist/glob/main.js', inject: 'libs'}
];
const seedDependencies = this.NPM_DEPENDENCIES;
this.NPM_DEPENDENCIES = seedDependencies.concat(additional_deps);
}
}
Now I can use it in my angular 2 service:
import { Injectable } from '#angular/core';
declare var Socket: any;
declare var Channel: any;
#Injectable()
export class PhoenixChannelService {
socket: any;
channel: any;
constructor() {
this.socket = new Socket("/socket", {
logger: ((kind, msg, data) => { console.log(`${kind}: ${msg}`, data) })
});
this.socket.connect({user_id: "123"});
}
}
Depending on your build process (I use gulp) and other factors you might have to adapt. But I hope this provides some help to other people stuck with this issue.
Edit: This is the official extracted JS client for Phoenix. But I didn't get it to work in my setup, probably because of Typescript.