JavaScript: Evaluating export code directly from string - javascript

How does one run and resolve a JavaScript export module directly from a string?
Here's a concrete example: The module vue-route-generator generates export code that returns a Vue Router configuration object. So when I issue this command:
const routes = require('vue-route-generator').generateRoutes(...);
routes is a string containing code, something like this:
function index() {
return import('/home/jay/Documents/industrial/resources/js/sections/index.vue');
}
function people() {
return import('/home/jay/Documents/industrial/resources/js/sections/people.vue');
}
export default [
{ name: 'index', path: '/', component: index },
{ name: 'people', path: '/people', component: people },
];
How do I run this code and get the object without saving this code to a separate file? I'm using Webpack as my builder but a solution that works for other builders would be appreciated.

Related

Vue.js - Define path of vue route based on URL query params

Let's assume I have this URL
http://localhost:8080/public/form?code=fhuZ15aHy
I have defined a route in my route.js file like this
{
path: '/public/form?code=:code',
name: 'survey',
component: () => import('./views/form')
}
which is supposed to reuse the same component based on the code query parameter coming from URL.
In my form.vue component I have defined a prop like this:
props: {
code: {
type: String,
required: true
}
}
But this is not working, in the sense that I am not redirected to any page.
I think I'm doing wrong with how I defined the path of the route. How can I achieve this?
Do not define the query within the path
You should write like this:
{
path: '/public/form',
name: 'survey',
component: () => import('./views/form')
}
router.push({ path: '/public/form', query: { code } })
And within the component you can access the query like this
const {code} = this.$route.query

Vue, is there a way to pass data between routes without URL params?

I am looking how to pass data secretly between two separate components (not parent and child) without using URL params in my Vue2 app. This doesn't mean I am passing secrets but rather I just dont want the user to see it (only for UI considerations).
I know Vue has Props but they are meant for passing data between parent and child component.
In my case, my URL will change but I don't want to pass data via visible params.
Someone claimed to use props without URL params here but I haven't been able to reproduce a working solution (getting undefined each time).
I also checked out these options but they are all using either URL or query params which as we know are visible.
An ugly solution would be to write the data to local storage and then read it there but this creates a lot of overhead and complexity (like what if I only want this data to be read once, etc).
Is there a more elegant solution to this problem?
Thanks!
make props: true for the destination route -- in the index.js file of router
{
path: '/home',
name: 'home',
component: taskChooser,
props: true,
}
define prop in the component e.g props: ['myprop'], - note the quotes
copy the variable you want to pass from the source route into the same name as your prop - in this case myprop
myprop = theVariableThatYouWantToPass
this.$router.replace({name:'home', params:{myprop}});
Make sure that the name of prop and variable are same - the prop is in quotes.
It's working for me.
Thanks #Mayank for pointing me in the correct direction.
Here is the correct syntax that worked for me.
Notice the props in In router index
{
path: '/componentPath',
name: 'componentName',
props: {
header: true,
content: true
},
}
In the component you are redirecting to, define the props as following:
props: {
myProperty: {
type: <DATATYPE>
},
}
Perform redirect as following:
this.$router.push({
name: 'componentName',
params: {
myProperty: <VARIABLE>
}
})
Access props with the this. convention from created or in later lifecycle event.
In this case, the variable name and property name do not have to be the same as it is a simple map. That naming convention would be a curious design choice anyway.
I haven't tested this in Vue 2, but in Vue 3, you can pass a stringified object through the props when you click on a link:
Add props: true to your routes file, for the route.
{
path: 'receipt',
name: 'receipt',
component: () => import('../pages/Receipt.vue'),
props: true,
beforeEnter(to, from, next) {
if (!to.params.receiptData) {
return next({
name: 'dashboard',
params: {
locale: from.params.locale ? from.params.locale : 'en',
},
});
}
return next();
},
},
Include your stringified object as a param for router.push().
const receiptData = {
transferType: 'default',
recipient: receiver.value.name,
referenceNumber: '#B3423424234',
amountSent: formAmount,
transferFee: 0,
};
router.push({
name: 'receipt',
params: {
receiptData: JSON.stringify(receiptData),
},
});
Declare the props as instance data in the component.
<script setup>
import { computed } from 'vue';
const props = defineProps({
receiptData: {
type: String,
required: true,
},
})
console.log('receiptData', props.receiptData);
const parsedReceiptData = computed(() => JSON.parse(props.receiptData));
</script>
I haven't tested an upper limit for size, so be careful about passing a huge object through, and you'll notice I showed a beforeEnter middleware on the route too because, if the user presses F5 to refresh the page, the props will be lost, so in my case, I redirect the user away from the page because the receipt is for one time use only.

How to replace a string value using Vue.js mixins?

I have created a mixins file called as urlMixin.js as show below,
module.exports = {
data() {
return {
url: 'http://localhost:3000'
};
}
}
I have a component called as profile.vue in which I have imported the mixin file. The profile.vue file looks like this,
import axios from 'axios';
import urlMixin from './../../mixins/urlMixin';
export default{
data() {
return{
}
},
created: function(){
},
mixins : [ urlMixin ],
methods:{
getInfo: function(){
axios.get('this.url/profile')
.then(response =>{
})
.catch(e => {
this.errors.push(e);
})
}
}
}
I want to be able to replace the url value in the Profile.vue with the url value present in urlMixin.js file, like how it is shown in the profile.vue file. Is there a way using which I can achieve this?
If you are using ES6 you can use Template Literals to insert the value into the string:
axios.get(`${this.url}/profile`)
Or using plain Javascript:
axios.get(this.url + '/profile')

Export additional interfaces for CommonJS module (Typescript)

I'm trying to use a simple JS library in Typescript/React, but am unable to create a definition file for it. The library is google-kgsearch (https://www.npmjs.com/package/google-kgsearch). It exports a single function in the CommonJS style. I can successfully import and call the function, but can't figure out how to reference the type of the arguments to the result callback.
Here is most of the library code:
function KGSearch (api_key) {
this.search = (opts, callback) => {
....
request({ url: api_url, json: true }, (err, res, data) => {
if (err) callback(err)
callback(null, data.itemListElement)
})
....
return this
}
}
module.exports = (api_key) => {
if (!api_key || typeof api_key !== 'string') {
throw Error(`[kgsearch] missing 'api_key' {string} argument`)
}
return new KGSearch(api_key)
}
And here is my attempt to model it. Most of the interfaces model the results returned by service:
declare module 'google-kgsearch' {
function KGSearch(api: string): KGS.KGS;
export = KGSearch;
namespace KGS {
export interface SearchOptions {
query: string,
types?: Array<string>,
languages?: Array<string>,
limit?: number,
maxDescChars?: number
}
export interface EntitySearchResult {
"#type": string,
result: Result,
resultScore: number
}
export interface Result {
"#id": string,
name: string,
"#type": Array<string>,
image: Image,
detailedDescription: DetailedDescription,
url: string
}
export interface Image {
contentUrl: string,
url: string
}
export interface DetailedDescription {
articleBody: string,
url: string,
license: string
}
export interface KGS {
search: (opts: SearchOptions, callback: (err: string, items: Array<EntitySearchResult>) => void) => KGS.KGS;
}
}
}
My issue is that from another file I am unable to reference the KGS.EntitySearchResult array returned by the search callback. Here is my use of the library:
import KGSearch = require('google-kgsearch');
const kGraph = KGSearch(API_KEY);
interface State {
value: string;
results: Array<KGS.EntitySearchResult>; // <-- Does not work!!
}
class GKGQuery extends React.Component<Props, object> {
state : State;
handleSubmit(event: React.FormEvent<HTMLFormElement>) {
kGraph.search({ query: this.state.value }, (err, items) => { this.setState({results: items}); });
event.preventDefault();
}
....
}
Any suggestions for how to make the result interfaces visible to my calling code without messing up the default export is very greatly appreciated.
The issue here is easily resolved. The problem is that while you have exported KGSearch, you have not exported the namespace KGS that contains the types. There are several ways to go about this, but the one I recommend is to take advantage of Declaration Merging
Your code will change as follows
declare module 'google-kgsearch' {
export = KGSearch;
function KGSearch(api: string): KGSearch.KGS;
namespace KGSearch {
// no changes.
}
}
Then from consuming code
import KGSearch = require('google-kgsearch');
const kGraph = KGSearch(API_KEY);
interface State {
value: string;
results: Array<KGSearch.EntitySearchResult>; // works!!
}
Unfortunately, whenever we introduce an ambient external module declaration, as we have by writing declare module 'google-kgsearch' at global scope, we pollute the global namespace of ambient external modules (that is a mouthful I know). Although it is unlikely to cause a conflict in your specific project for the time being, it means that if someone adds an #types package for google-kgsearch and you have a dependency which in turn depends on this #types package or if google-kgsearch every starts to ship its own typings, we will run into errors.
To resolve this we can use a non-ambient module to declare our custom declarations but this involves a bit more configuration.
Here is how we can go about this
tsconfig.json
{
"compilerOptions": {
"baseUrl": "." // if not already set
"paths": { // if you already have this just add the entry below
"google-kgsearch": [
"custom-declarations/google-kgsearch"
]
}
}
}
custom-declarations/google-kgsearch.d.ts (name does not matter just needs to match paths)
// do not put anything else in this file
// note that there is no `declare module 'x' wrapper`
export = KGSearch;
declare function KGSearch(api: string): KGSearch.KGS;
declare namespace KGSearch {
// ...
}
This encapsulates us from version conflicts and transitive dependency issues by defining it as an external module instead of an ambient external module.
One last thing to seriously consider is sending a pull request to krismuniz/google-kgsearch that adds your typings (the second version) in a file named index.d.ts. Also, if the maintainers do not wish to include them, consider creating an #types/google-kgsearch package by sending a pull request to DefinitelyTyped

loadChildren syntax - what is hash part

Angular in the docs says the following:
{
path: 'admin',
loadChildren: 'app/admin/admin.module#AdminModule',
},
I'm curious about the syntax. As I understand, this part:
app/admin/admin.module
defines the path to load a module. But what is this #AdminModule?
I'm reading this article, and there is the following path:
loadChildren: 'contacts.bundle.js',
so, as you can see, there is nothing with hash.
The hash part denotes the exported module name. So, inside app/admin/admin.module the AdminModule is exported:
export class AdminModule {}
However, if default export is used, there is no need to use hash.
Here is the relevant part from the sources system_js_ng_module_factory_loader.ts:
private loadAndCompile(path: string): Promise<NgModuleFactory<any>> {
let [module, exportName] = path.split(_SEPARATOR);
if (exportName === undefined) exportName = 'default';
return System.import(module)
.then((module: any) => module[exportName])
.then((type: any) => checkNotEmpty(type, module, exportName))
.then((type: any) => this._compiler.compileModuleAsync(type));
}

Categories

Resources