VueJS dynamic/async loading of component - asynchronous

Version of Vue: 2.4.2
Version of webpack: 2.6.1
The task is... I have a component. It gets global variable, a string. And depends on this variable it should dynamically load and show one of seven quite heavy components. It seems to me that it's not a good idea to import it all because of their weight.
Now I've made import like this:
import comp1 from '#/components/comp1.vue';
import comp2 from '#/components/comp2.vue';
export default {
components: {comp1, comp2}
...
}
In the template we'll use it like <comp1></comp1> And this is not what I want. The output file is pretty big and I must move on.
I've tried made like this described in docs:
export default {
components: {comp1: () => import('#/components/comp1.vue')}
...
}
A-a-and we have an error:
[Vue warn]: Failed to resolve async component: function comp1() {
return __webpack_require__.e/* import() */(1).then(__webpack_require__.bind(null, 37));
}
Reason: Error: Loading chunk 1 failed.
The initial idea was:
let param = window.GLOBAL_PARAM;
let str = '#/components/comp2.vue';
switch(param) {
case 1: {
str = '#/components/comp1.vue';
break;
}
case 2: {
str = '#/components/comp3.vue';
break;
}
...
}
export default {
components: {comp: () => import(str)}
...
}
But I've got an error:
[Vue warn]: Failed to resolve async component: function comp() {
return __webpack_require__(50)(componentPath);
}
Reason: Error: Cannot find module '#/components/comp2.vue'.
As I understand, I have a problem with webpack, which can't resolve "#" symbol
So, the questions are:
How should I fix up async loading off my components? Maybe I've made a
sad mistake in my code?
How should I organize by the right way the loading one of the seven components depending on some condition (global variable or param in component). Maybe there are some webpack preferences and/or some plugins. Or maybe I should resole this problem by the other approach?

Related

SQL with Prisma under Electron

My Main goal is to create an Electron App (Windows) that locally stores data in an SQLite Database. And because of type safety I choose to use the Prisma framework instead of other SQLite Frameworks.
I took this Electron Sample Project and now try to include Prisma. Depending on what I try different problems do arrise.
1. PrismaClient is unable to be run in the Browser
I executed npx prisma generate and then try to execute this function via a button:
import { PrismaClient } from '#prisma/client';
onSqlTestAction(): void {
const prisma = new PrismaClient();
const newTestObject = prisma.testTable.create(
{
data: {
value: "TestValue"
}
}
);
}
When executing this in Electron I get this:
core.js:6456 ERROR Error: PrismaClient is unable to be run in the browser.
In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues
at new PrismaClient (index-browser.js:93)
at HomeComponent.onSqlTestAction (home.component.ts:19)
at HomeComponent_Template_button_click_7_listener (template.html:7)
at executeListenerWithErrorHandling (core.js:15281)
at wrapListenerIn_markDirtyAndPreventDefault (core.js:15319)
at HTMLButtonElement.<anonymous> (platform-browser.js:568)
at ZoneDelegate.invokeTask (zone.js:406)
at Object.onInvokeTask (core.js:28666)
at ZoneDelegate.invokeTask (zone.js:405)
at Zone.runTask (zone.js:178)
It somehow seems logical that Prisma cannot run in a browser. But I actually build a native app - with Electron that embeds a Browser. It seems to be a loophole.
2. BREAKING CHANGE: webpack < 5 used to include polyfills
So i found this Question: How to use Prisma with Electron
Seemed to be exactly what I looked for. But the error message is different (Debian binaries were not found).
The solution provided is to generate the prisma artifacts into the src folder instead of node_modules - and this leads to 19 polyfills errors. One for example:
./src/database/generated/index.js:20:11-26 - Error: Module not found: Error: Can't resolve 'path' in '[PATH_TO_MY_PROJECT]\src\database\generated'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "path": require.resolve("path-browserify") }'
- install 'path-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "path": false }
And this repeats with 18 other modules. Since the error message to begin with was different I also doubt that this is the way to go.
I finally figured this out. What I needed to understand was, that all Electron apps consist of 2 parts: The Frontend Webapp (running in embedded Chromium) and a Node backend server. Those 2 parts are called IPC Main and IPC Renderer and they can communicate with each other. And since Prisma can only run on the main process which is the backend I had to send my SQL actions to the Electron backend and execute them there.
My minimal example
In the frontend (I use Angular)
// This refers to the node_modules folder of the Electron Backend, the folder where the main.ts file is located.
// I just use this import so that I can use the prisma generated classes for type safety.
import { TestTable } from '../../../app/node_modules/.prisma/client';
// Button action
onSqlTestAction(): void {
this.electronService.ipcRenderer.invoke("prisma-channel", 'Test input').then((value) => {
const testObject: TestTable = JSON.parse(value);
console.log(testObject);
});
The sample project I used already had this service to provide the IPC Renderer:
#Injectable({
providedIn: 'root'
})
export class ElectronService {
ipcRenderer: typeof ipcRenderer;
webFrame: typeof webFrame;
remote: typeof remote;
childProcess: typeof childProcess;
fs: typeof fs;
get isElectron(): boolean {
return !!(window && window.process && window.process.type);
}
constructor() {
// Conditional imports
if (this.isElectron) {
this.ipcRenderer = window.require('electron').ipcRenderer;
this.webFrame = window.require('electron').webFrame;
this.childProcess = window.require('child_process');
this.fs = window.require('fs');
// If you want to use a NodeJS 3rd party deps in Renderer process (like #electron/remote),
// it must be declared in dependencies of both package.json (in root and app folders)
// If you want to use remote object in renderer process, please set enableRemoteModule to true in main.ts
this.remote = window.require('#electron/remote');
}
}
And then in the Electron backend I first added "#prisma/client": "^3.0.1" to the package.json (for the Electron backend not the frontend). Then I added to the main.ts this function to handle the requests from the renderer:
// main.ts
ipcMain.handle("prisma-channel", async (event, args) => {
const prisma = new PrismaClient();
await prisma.testTable.create(
{
data: {
value: args
}
}
);
const readValue = await prisma.testTable.findMany();
return JSON.stringify(readValue);
})
This way of simply adding the IPC Main handler in the main.ts file of course is a big code smell but usefull as minimal example. I think I will move on with the achitecture concept presented in this article.

Deno: TS2345 [ERROR]: Argument of type 'BufReader' is not assignable to parameter of type 'string | BufReader'

I'm exploring Deno with cvs files, but i got a problem with the parse.
I'm thinking that the problem come with the old dependencies;
deno info --unstable --no-check .\mod.ts
The majority are from #0.75 and I'm in the #1.5.
And I want to collaborate to help to solve problems in the Deno repo and not just downgrade to some stable version.
I'm using:
deno 1.5.0
v8 8.7.220.3
typescript 4.0.
I'm having having issue with this code:
import { join } from "https://deno.land/std/path/mod.ts";
import { parse } from "https://deno.land/std/encoding/csv.ts";
import { BufReader } from "https://deno.land/std/io/bufio.ts";
async function loadData() {
const path = join(".", "data.csv");
const file = await Deno.open(path);
const bufReader = new BufReader(file);
const result = await parse(bufReader, {
skipFirstRow: true,
comment: "#",
});
Deno.close(file.rid)
console.log(result)
}
await loadData();
The error:
error: TS2345 [ERROR]: Argument of type 'BufReader' is not assignable to parameter of type 'string | BufReader'.
Type 'import("https://deno.land/std/io/bufio.ts").BufReader' is not assignable to
type 'import("https://deno.land/std#0.75.0/io/bufio.ts").BufReader'.
Types have separate declarations of a private property 'buf'.
const result = await parse(bufReader, {
~~~~~~~~~
at...
In Deno #1.0.0 works fine
deno upgrade --version 1.0.0
And in #1.5.1 works fine too
deno upgrade --version 1.5.1
1.5.0 had many problems due to a change in the nature of the compiler. I suggest downgrading to a lower version or ultra to 1.5.1 to receive the fixes implemented later on.
You should import
import { BufReader } from "https://deno.land/std#0.75.0/io/bufio.ts";
instead of
import { BufReader } from "https://deno.land/std/io/bufio.ts";
Better approach:
But actual best practice in case of deno is to use deps.ts file for all your dependencies.
Create file deps.ts in the root directory and export your dependencies there.
// deps.ts
// Standard library dependencies
export { BufReader } from "https://deno.land/std#0.75.0/io/bufio.ts";
// Third party dependencies
// ...
and in your other files you should import from deps.ts. For example, if you had mod.ts file in the root directory:
import { BufReader } from "./deps.ts";
// ...
Good thing about this approach is that whenever you want to change version of your imported libraries you will have to change in one place instead of many.

Angular 2 submodule routing with AOT and Rollup

What is the correct way to configure a submodule in Angular 2 so that it will work after AOT and rollup? I'm not concerned about lazy loading and will be happy for all submodules to be bundled together, but loadChildren is the cleanest way of referencing a submodule and having it use the correct <router-outlet>, and although I've tried various methods, none will work in both development and production.
I followed the instructions in the AOT cookbook to prepare my app for deployment. My module structure is root > account > admin, and I want the admin routes to load in the outlet defined by the account component.
Here's the router config for the account module:
import { NgModule } from '#angular/core';
import { AdminModule } from './admin/admin.module';
const accountRoutes: Routes = [{
path: 'account',
component: AccountComponent,
children: [
{ path: 'setup', component: SetupComponent },
{ path: 'admin', loadChildren: () => AdminModule }
]
}];
This works in development, but NGC compilation fails with Error: Error encountered resolving symbol values statically. Reference to a local (non-exported) symbol 'accountRoutes'.
I added an export to accountRoutes but this also fails to compile with Error: Error encountered resolving symbol values statically. Function calls are not supported..
One suggestion was to use a string instead of a function so that NGC can compile the code:
loadChildren: 'app/account/admin/admin.module#AdminModule'
This works in development and compiles successfully but the compiled app will not run, because SystemJS is unavailable. The error is:
ERROR Error: Uncaught (in promise): TypeError: System.import is not a function
TypeError: System.import is not a function
at t.loadFactory (build.js:6)
at t.load (build.js:6)
at t.loadModuleFactory (build.js:12)
I tried including SystemJS in the production build but it could not find the separate module file app/account/admin/admin.module.ngfactory - after rollup everything is in one build.js. There may be a way to have separate rollups build each submodule, but that's a lot of work.
I found the suggestion of referring to an exported function:
export function loadAdminModule() {
return AdminModule;
}
loadChildren: loadAdminModule
This works in development, but in production the runtime compiler is unavailable so it logs build.js:1 ERROR Error: Uncaught (in promise): Error: Runtime compiler is not loaded.
The helper function can be modified so it works in production by referencing the NgFactory instead, but this won't work in development.
import { AdminModuleNgFactory } from '../../../aot/src/app/account/admin/admin.module.ngfactory';
export function loadAdminModule() {
return AdminModuleNgFactory;
}
loadChildren: loadAdminModule
Is there a supported way of using loadChildren so that it will work with the instructions in the AOT cookbook? Is it better to use webpack instead?
For anyone having the same problem, here's the temporary workaround I'm using. Hopefully a better answer will come along soon.
I now define my submodules in a dedicated file, submodules.ts. I have two versions of this file, submodules-jit.ts for development, and submodules-aot.ts for AOT/rollup deployment to production. I gitignore submodules.ts and have added commands to my npm start and npm build:aot scripts that substitute in the right one.
// submodules-jit.ts
import { AdminModule } from './account/admin/admin.module'
export function adminModule(): any { return AdminModule; }
// submodules-aot.ts
import { AdminModuleNgFactory } from '../../aot/src/app/account/admin/admin.module.ngfactory'
import { AdminModule } from './account/admin/admin.module'
export function adminModule(): any { return AdminModuleNgFactory; }
export function adminModuleKeep(): any { return AdminModule; }
AdminModule must be referenced in the AOT file so it is preserved.
Then I use the adminModule function in my routes:
import { adminModule } from '../submodules'
{ path: 'admin', loadChildren: adminModule }
Finally for convenience, npm scripts drop in the correct file.
"build:aot": "cp src/submodules-aot.ts src/app/submodules.ts && ngc -p tsconfig-aot.json && rollup -c rollup-config.js",
"start": "cp src/submodules-jit.ts src/app/submodules.ts && concurrently \"npm run build:watch\" \"npm run serve\""
Needless to say this is a horrible hack, but it keeps the routing files clean and most of the evil in one place.
In my case I solved with your hint on loading routes from an external module, then reading this issue on angular/cli issue page.
So, I came out refactoring my routes from:
import { TestModule } from './pathToModule/test.module';
export function exportTestModule() {
return TestModule;
}
. . .
{
path: 'test',
loadChildren: exportTestModule
}
To:
. . .
{
path: 'test',
loadChildren: './pathToModule#TestModule'
}

How do I correctly import my collection definition?

How do I correctly import my collection definition?
I get this error message when as a result of trying to import
I externalized my collection definition FROM the main myMeteorApp.js file:
(My directory structure looked like this:)
/myMeteorApp
/myMeteorApp.js
...TO the tasks.js file:
(My directory structure currently looks like this:)
/myMeteorApp
--/imports/api/tasks.js
The contents of tasks.js look like this:
import { Mongo } from "meteor/mongo";
const Images = new FS.Collection("images", {
stores: [new FS.Store.FileSystem("images", {path: "~/uploads"})]
});
const buyList = new Mongo.Collection("BuyList");
const WhoAreWe = new Mongo.Collection("whoDb");
const merchantReviews = new Mongo.Collection("MerchantReviews");
const Messages = new Meteor.Collection("messages", {transform: function (doc) { doc.buyListObj = buyList.find({sessionIDz: {$in: [doc.buyList]}}); return doc; }});
export { Images };
export { buyList };
export { WhoAreWe };
export { merchantReviews };
export { Messages };
I have packages babel-preset-es2015 and ecmascript installed, but these haven't helped.
Looking forward to your help...
Everything is the chat session we had indicates that your original app used Meteor 1.2, which did not support ES2015 modules. In addition, you did not import them correctly.
Here is a short checklist for import-related issues:
Make sure that your project is actually using Meteor v1.3+ (run meteor --version). Earlier versions did not support the modules feature.
Make sure that you have the ecmascript package installed (run meteor list or cat .meteor/packages | grep ecmascript from your project's root directory. If not, meteor add ecmascript. This is the package used for compiling ES2015.
Make sure that you are using it correctly. There are 2 types of exports:
default - import foo from 'imports/bar' goes with export default bar.
named - import { foo } from 'imports/bar' goes with export const foo = ..., etc.

Angular 2.0 - Meteor UploadFS Error

I am working on the Angular 2.0-Meteor tutorial and on step 20 "Handling Files with CollectionFS" I am getting an error.
"Cannot find module 'meteor/jalik:ufs'." I have tried removing and adding jalik:ufs and calling meteor reset but this error seems to persist.
I get the error when trying to run the sample code included before Step 21 as well.
It is related with typings. Right now I don't think there is an existing typings for this package.
So you can write your own typings.
Or use the temporary way to remove the warning:
Remove import { UploadFS } from 'meteor/jalik:ufs';. Then add declare const UploadFS: any; in any file.
Tutorial was updated in between:
See Point 21.22 Declare meteor/jalik:
Link
declare module "meteor/jalik:ufs" {
interface Uploader {
start: () => void;
}
interface UploadFS {
Uploader: (options: any) => Uploader;
}
export var UploadFS;
}

Resources