This looks like destructuring:
const {getElementById, seedElements} = require('./utils')
but I'm confused about it. I'm used to seeing something like:
let {first, last} = name
Are these doing the same things just in different files?
You can think
const {getElementById, seedElements} = require('./utils')
as destructuring since when you export, you would write your export like
module.exports = { getElementById, seedElements };
or
export { getElementById, seedElements };
and while importing using require you would basically be importing the entire module and you can destructure the individual modules from it.
const {getElementById, seedElements} = require('./utils')
would be similar to
const Utils = require('./utils');
const { getElementById, seedElements } = Utils;
with the import syntax, you would however import the named exports like
import { getElementById, seedElements } from './utils';
Yes, that is object destructuring.
The require() function in Node.js can be used to import modules, JSON, and local files. For instance (from the docs):
// Importing a local module:
const myLocalModule = require('./path/myLocalModule');
Calling require(moduleId) returns the object module.exports of moduleId ( module.exports contains precisely all properties that are made available by the module).
Related
I have a Vue 3 app (without TypeScript), and I'm using a Vuex 4 store.
I have my store separated into modules that represent some common themes, for example I have a user module that contains the store, getters, actions etc that are appropriate for dealing with a user. But, I also have some common functionalities that is present in all of my modules, and it's an obvious place where I could simplify my modules and thin them out a bit.
Let's say for example that I have a generic set() mutator, like below:
const mutations = {
set(state, payload) {
state[payload.key] = payload.data;
}
}
This will simply take in a payload and populate whatever store object the payload 'key' field belongs to, and it works well when I want to simply set a single field in my store. Now, the issue is that this set() function is being duplicated in every single store module that I have since it's just a generic store mutation, and once the number of modules I reach increases a bit, you can imagine that it's quite wasteful and pointless to include this mutation every single time.
I'm not sure how to implement this, however.
My current main store file looks like this (simplified for the sake of the question):
// store/index.js
import { createStore } from "vuex";
import module1 from "./modules/module1";
import module2 from "./modules/module2";
export const store = createStore({
modules: { module1, module2 },
});
All of my modules are implemented in the same exact way:
// store/modules/module1.js
const state = { };
const mutations = { set(state, payload) };
const actions = { };
const getters = { };
export default {
namespaced: true,
state,
getters,
actions,
mutations
};
And then somewhere in a component or wherever I'd call the specific module action/mutation/store I need with the below:
...mapMutations: ({ set: "module1/set" })
What would be the best way for me to bring that shared functionality out into a singular place, and how would I properly use it if I, for example, wanted to set and mutate the store in module1 and module2 at the same time properly? The part that I'm not sure about is how exactly I could call a generic mutation and have it target the desired module's state
Thanks to this answer linked by #Beyers, I've implemented the store in a similar fashion to this:
// store/sharedModuleMethods.js
export default class {
constructor() {
this.mutations = {
set(state, payload) {}
}
}
}
And then in the modules, I just instantiate the class and spread the methods where they need to be
// store/modules/module1.js
import SharedModuleMethods from "../sharedModuleMethods";
const methods = new SharedModuleMethods()
const mutations = {
...methods.mutations,
// Module specific mutations go here
};
And then in whatever component I need to set() something, I can just call the mutation like I would before
...mapMutations: ({ setModule1: "module1/set", setModule2: "module2/set" })
It's not quite as automated and streamlined as I'd personally prefer (define it once, have the modules magically have all the methods available to them via a flag or something), but alas life isn't perfect either.
I need to use an API that requires initialization with an API key and some other details within my Vue.js app.
var client = api_name('app_id', 'api_key', ...)
I would need to make several API calls with the client object in multiple components in my app
client.api_function(...)
How can I avoid repeating the initialization step in every component?
I'm thinking about using a global mixin in main.js for that
Vue.mixin({
data: function() {
return {
get client() {
return api_name('app_id', 'api_key');
}
}
}
})
Is this a good approach?
I'd rather move your getter to a service and just import, where you actually need it. It doesn't seem to fit into data section, more like methods. A mixin is a decent approach if you need lots of similar stuff: variables, methods, hooks etc. Creating a mixin for only 1 method looks like overkill to me.
// helper.js
export function getClient () {
// do stuff
}
// MyComponent.vue
import { getClient } from 'helpers/helper`
// Vue instance
methods: {
getClient
}
How about creating a helper file and writing a plugin that exposes your api url's? You can then create prototypes on the vue instance. Here's an example,
const helper = install(Vue){
const VueInstance = vue
VueInstance.prototype.$login = `${baseURL}/login`
}
export default helper
This way you can access url's globally using this.$login. Please note $ is a convention to avoid naming conflicts and easy to remember that it is a plugin.
in my angular app I have the following:
export class MyComponent {
subcompPath = "path-to-subcomp#SubcompClassName";
#ViewChild("placeholder", { read: ViewComponentRef }) placeholderRef: ViewComponentRef;
/* Constructor where Compiler, ComponentFactoryResolver are injected */
loadSubcomponent() {
let [path, componentName] = this.subcompPath.split("#");
(<any>window).System.import(path)
.then((module: any) => module[componentName])
.then((type: any) => {
return this._compiler.compileComponentAsync(type)
})
.then((factory: any) => {
let componentRef = this.placeholderRef.createComponent(factory, 0);
});
}
}
My sub-component declares providers and stuff, directives and pipes.
And now RC6 is out to break everything yet again. Components can't declare directives and pipes, but they must be in the module where the component is declared.
So I have to load with SystemJS not the component itself but the module.
Ok, and then I should use
return this._compiler.compileModuleAndAllComponentsAsync(type)
Fine, but how do I get a reference to the factory of this specific component? That factory is all I need, the placeholderRef wants it in its createComponent method, right?
I tried to dig into the angular2 source code from github but it's quite vast, I should try from VS Code or something, with intellisense, but I'm lazy...and I should read this stuff from documentation, which is quite lackluster in angular.io for this particular argument, which is lazy loading of components and modules WITHOUT the router.
Any help is appreciated, I think the solution is simple to apply but hard to find without official documentation.
Update:
If you want to use it together with aot compilation you should manually provide Compiler like
export function createJitCompiler () {
return new JitCompilerFactory([{useDebug: false, useJit: true}]).createCompiler();
}
...
providers: [
{ provide: Compiler, useFactory: createJitCompiler}
],
Example
Old version
It might help you:
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
.then(({moduleFactory, componentFactories}) => {
const compFactory = componentFactories
.find(x => x.componentType === DynamicComponent);
const cmpRef = this.placeholderRef.createComponent(compFactory, 0);
See also
related answer with example
How to use variable to define templateUrl in Angular2
sample code from angular2 source code
Based on yurzui's answer I've come to the following code:
export class MyComponent {
subcompPath = "path-to-subcompMODULE#SubcompClassName";
#ViewChild("placeholder", { read: ViewComponentRef }) placeholderRef: ViewComponentRef;
/* Constructor where Compiler, ComponentFactoryResolver are injected */
loadSubcomponent() {
let [modulePath, componentName] = this.subcompPath.split("#");
(<any>window).System.import(modulePath)
.then((module: any) => module["default"]) // Or pass the module class name too
.then((type: any) => {
return this._compiler.compileModuleAndAllComponentsAsync(type)
})
.then((moduleWithFactories: ModuleWithComponentFactories<any>) => {
const factory = moduleWithFactories.componentFactories.find(x => x.componentType.name === componentName); // Crucial: componentType.name, not componentType!!
let componentRef = this.placeholderRef.createComponent(factory, 0);
});
}
}
For example, in CommonJS:
var actions = module.exports = Flux.createActions({
SubmitSignup: function(payload) {
utils.xhr('POST', payload.url, payload.data, function(response, status){
actions.ReceiveSignupResponse({
response: response,
status: status,
receiver: payload.receiver
});
});
return payload;
},
ReceiveSignupResponse: function(payload) {
payload.receiver.handleSignupResponse(payload.response, payload.status);
return payload;
}
});
This exports the value of the Flux.createActions(...) expression as the module, and also assigns it to the local variable actions which is used internally so actions can refer to each-other. Even without the multi-assignment one-liner, the value of the expression would still be available as module.exports.
ES6 still allows for exporting a single value for the module using export default and even allows for assigning a local name to that value for classes and functions:
export default function foo() {...}
// or
export default class Foo { ...}
It also allows arbitrary expressions to be exported (export default (whatever());) and it allows local assignments to be exported as well (export let actions = Flux.createActions({...});), retaining the local name for local use, however, it does not allow exporting an assignment expression as a default - the following are all invalid:
export default let actions = Flux.createActions({...});
export default (let actions = Flux.createActions({...}));
export default as actions = Flux.createActions({...});
Nor is there a modules.exports equivalent that could be used to reference the value internally (in practice, tools like babel and other transpilers will let you mix-and match CommonJS and ES6 modules as you will, so modules.export will, in fact, work, but this is non-standard, almost accidental behavior).
It is of course still perfectly possible to simply declare the local first and export it later:
let actions = Flux.createActions({
SubmitSignup: function(payload) {
utils.xhr('POST', payload.url, payload.data, function(response, status){
actions.ReceiveSignupResponse({
response: response,
status: status,
receiver: payload.receiver
});
});
return payload;
},
ReceiveSignupResponse: function(payload) {
payload.receiver.handleSignupResponse(payload.response, payload.status);
return payload;
}
});
export default actions;
And that's fine. It just seems like a strange incongruity that you can export and assign local names for any non-default values, including arbitrary expressions, as well as default functions and classes, just not expressions.
Is there a better way of doing this that I'm missing?
Other than importing itself, I don't think there is.
The reason pretty much is that default is no valid identifier, so if you have you have to choose your own identifier anyway then you'll have to use the standard way of explicitly exporting:
let x = …;
export { x as y };
export default x;
Notice that export let x = … is only syntactic sugar for export { x as x }, i.e. where the local and exported name are the same, which for a default export never is the case.
Btw, default-exporting an assignment is indeed valid, you just can't use a variable declaration. Try
let actions;
export default (actions = Flux.createActions({...}));
I'm developing an NPM package using typescript. Within this package the TS files are setup as external modules. The compiler won't generate a single .d.ts for external modules. I'm trying to concat all tsc generated type definitions into a single .d.ts for the entire package.
I'm having issues laying out the single .d.ts file (following a similar approach to that used in grunt-dts-bundle). The condensed example below captures my issue.
Given this external module declaration and test file:
test.d.ts :
declare module "ExternalWrapper" {
export import Foo = require("FooModule");
}
declare module "FooModule" {
class Foo {
name: string;
}
export = Foo;
}
test.ts:
import externalWrapper = require( 'ExternalWrapper' );
var t = new externalWrapper.Foo();
Running tsc test.ts test.d.ts -m commonjs produces this error: TS2083: Invalid 'new' expression.
If you change 'test.ts' to look like this, importing 'FooModule' directly:
import Foo = require( "FooModule" );
var t = new Foo();
It compiles fine.
The compiler understands the type externalWrapper.Foo however it doesn't seem to represent it as the same type FooModule.Foo. There is something I'm not getting about how the compilers handles modules that are exported via 'export import'.
Failing the above I'll probably look to manually creating the .d.ts :(
Any help appreciated.
You are probably missing a reference tag:
/// <reference path="test.d.ts"/>
It works :
You should be able to fix this by modifying your .d.ts file to resemble the following:
declare module "ExternalWrapper" {
import FooModule = require("FooModule");
export var Foo: typeof FooModule;
}
declare module "FooModule" {
class Foo {
name: string;
}
export = Foo;
}
With the export import syntax the compiler was assuming you were exporting an instance of Foo, not Foo itself... a little quirky.