How to create a global string in a vue (webpack) project - javascript

In my vue project I have a default image that should show up in places around the app when an image is missing or hasn't been uploaded yet. I have various class files and component calls that want to use the same image.
What is the best way to create a global variable that I can access from anywhere? I can't put it in the store because my class files (I'm using vuex-orm) are loaded before the store is declared. I would prefer not to put it in the window because I want to have a single word variable that I can call (defaultImg as opposed to window.my_project.globals.defaultImg). I could put it in .env but I want this to be checked into the repository and the same across environments so that doesn't seem right either.
What is the best way to provide a simple global string to all files in my vue project?

Are you using Vue 2 or 3? TL;DR best approach is to create a component.
Quick Fix
You could take the approach described in the documentation for creating plugins:
https://vuejs.org/guide/reusability/plugins.html#introduction
If you didn't want to go down the convoluted route of creating a separate plugin file just to store a single string you could probably just do something like this in your entry file where you initialize the app:
Vue 3:
app.config.globalProperties.$defaultImg = '/path/to/image.png'
Vue 2:
Vue.prototype.$defaultImg = '/path/to/image.png'
And use this in your templates like
<img :src="$defaultImage">
Best Solution
However, I think the best and the most 'Vue' way would be to create an image component which displays a given image, or the default image if src is nullish:
Vue 2 & 3
<template>
<img :src="srcOrDefault">
<template>
Vue 3 Composition API:
<script>
const defaultImg = '/path/to/image.png'
const props = defineProps({
src: String
});
const srcOrDefault = computed(() => {
return props.src || defaultImg
})
</script>
Vue 2 & Vue 3 Options API:
<script>
const defaultImg = '/path/to/image.png'
export default {
props: {
src: String
},
computed: {
srcOrDefault() {
return this.src || defaultImg
}
}
}
</script>

Related

Importing / exporting Javascript Object Properties

I support a relatively complex legacy codebase, but am looking to modernise it a little by bringing in Webpack so that we'd have import & export capabilities in JS.
The problem I'm having is that we use a global object called App where we define and add different properties depending on the page. So for example we have the following file where we instantiate App (loaded on all pages):
app.js
const App = (() => {
const obj = {
Lib: {},
Util: {},
// etc
}
return obj;
})();
Then in another file we add to App.Lib just for the specific page that needs it:
lazyload.js
App.Lib.Lazyload = (() => {
// lazyload logic
})();
We simply concatenate the files during the bundling process, but obviously this is not ideal as none of the files have no knowledge of what goes on outside of it.
Exporting only seems to work for the top level object (where the object is defined), so anything I add to it elsewhere cannot be exported again. For example if I add export default App.Lib.Lazyload; at the end of lazyload.js and then try to import it elsewhere it will not import the Lazyload property.
Is there any way to get this to work without major refactor? If not, would you have any suggestions about the best way to handle it?
I don't think you can import Object.properties in JS. If you want to bundle specific packages (say Lazyload) for packages that need them, you might try:
//lazyload.js
export const LazyLoad = {
//lazyload logic
}
then somewhere else...
import {LazyLoad} from 'path/to/lazyload.js';
// assuming App has already been created/instantiated
App.Lib.Lazyload = LazyLoad;
Using Export Default...
//lazyload.js
const LazyLoad = {};
export default LazyLoad;
then...
import LazyLoad from 'path/to/lazyload.js';
App.Lib.LazyLoad = LazyLoad;
You can find help with Imports and Exports at MDN.

Import dynamically named exports

Is it possible to import named exports dynamically?
I have a file, banana.js with hundreds of named exports. Id like to import them on demand. Is this possible? And if it is, will it only load that export and not all?
I know its possible to import them dynamically from individual files but I want them in the same file.
Example below..
// banana.js
export const banana_1 = {
..
}
export const banana_2 = {
..
}
// main.js
const currentPage = 1
async getSomething(){
let { `banana_${currentPage}` } = await import('./banana.js');
const foo = `banana_${currentPage}`
}
Fyi im using Vue.js
From what I know, you might have to use require('./banana.js') here. Please note that require is synchronous, so need for await. If you use eslint and it forbids usage of require, just add // eslint-disable-line to the end of that line. BTW, I don't know Vue at all.
About the object you import, you should probably make an array instead of suffixing each export with a number.
Edit: I just realized that dynamic imports are a thing not only possible with require, so ignore the first paragraph.
Based on your response to my question I offer the following solution. I understand not wanting to deploy and use a full database solution for the sake of hot loading some JSON objects. I'm unsure of your use case and other details related to your project.
You can use a self contained database like SQLite. Instead of setting up a NoSQL or SQL server. You can instead just store what you need to the SQLite file. Since Vue requires Node.js you can use the Node.js SQLite3 library https://www.npmjs.com/package/sqlite3.
I hope this helps.

Importing javascript file for use within vue component

I am working on a project that requires using a js plugin. Now that we're using vue and we have a component to handle the plugin based logic, I need to import the js plugin file within the vue component in order to initialize the plugin.
Previously, this was handled within the markup as follows:
<script src="//api.myplugincom/widget/mykey.js
"></script>
This is what I tried, but I am getting a compile time error:
MyComponent.vue
import Vue from 'vue';
import * from '//api.myplugincom/widget/mykey.js';
export default {
data: {
My question is, what is the proper way to import this javascript file so I can use it within my vue component?
...
Include an external JavaScript file
Try including your (external) JavaScript into the mounted hook of your Vue component.
<script>
export default {
mounted() {
const plugin = document.createElement("script");
plugin.setAttribute(
"src",
"//api.myplugincom/widget/mykey.js"
);
plugin.async = true;
document.head.appendChild(plugin);
}
};
</script>
Reference: How to include a tag on a Vue component
Import a local JavaScript file
In the case that you would like to import a local JavaScript in your Vue component, you can import it this way:
MyComponent.vue
<script>
import * as mykey from '../assets/js/mykey.js'
export default {
data() {
return {
message: `Hello ${mykey.MY_CONST}!` // Hello Vue.js!
}
}
}
</script>
Suppose your project structure looks like:
src
- assets
- js
- mykey.js
- components
MyComponent.vue
And you can export variables or functions in mykey.js:
export let myVariable = {};
export const MY_CONST = 'Vue.js';
export function myFoo(a, b) {
return a + b;
}
Note: checked with Vue.js version 2.6.10
try to download this script
import * from '{path}/mykey.js'.
or import script
<script src="//api.myplugincom/widget/mykey.js"></script>
in <head>, use global variable in your component.
For scripts you bring in the browser way (i.e., with tags), they generally make some variable available globally.
For these, you don't have to import anything. They'll just be available.
If you are using something like Webstorm (or any of the related JetBrains IDEs), you can add /* global globalValueHere */ to let it know that "hey, this isn't defined in my file, but it exists." It isn't required, but it'll make the "undefined" squiggly lines go away.
For example:
/* global Vue */
is what I use when I am pulling Vue down from a CDN (instead of using it directly).
Beyond that, you just use it as you normally would.
I wanted to embed a script on my component and tried everything mentioned above, but the script contains document.write. Then I found a short article on Medium about using postscribe which was an easy fix and resolved the matter.
npm i postscribe --save
Then I was able to go from there. I disabled the useless escape from eslint and used #gist as the template's single root element id:
import postscribe from 'postscribe';
export default {
name: "MyTemplate",
mounted: function() {
postscribe(
"#gist",
/* eslint-disable-next-line */
`<script src='...'><\/script>`
);
},
The article is here for reference:
https://medium.com/#gaute.meek/how-to-add-a-script-tag-in-a-vue-component-34f57b2fe9bd
For anyone including an external JS file and having trouble accessing the jQuery prototype method(s) inside of the loaded script.
Sample projects I saw in vanilla JS, React and Angular were simply using:
$("#someId").somePlugin(options)
or
window.$("#someId").somePlugin(options)
But when I try either of those in my VueJS component I receive:
Error: _webpack_provided_window_dot$(...).somePluginis not a function
I examined the window object after the resources had loaded I was able to find the jQuery prototype method in the window.self read-only property that returns the window itself:
window.self.$("#someId").somePlugin(options)
Many examples show how to load the external JS file in VueJS but not actually using the jQuery prototype methods within the component.

Accessing $route.params in VueJS

Looking through this documentation:
https://router.vuejs.org/en/essentials/navigation.html
It looks like you can bind the <router-link :to="variableName">Link Text</routerlink> Which is pretty nifty; however, I've had some trouble trying to access route parameters inside of a component I'm trying to build.
So I use this:
<router-link :to="permalink">Title of thing</router-link>
To then direct the router view to pull the forum thread. Using this in the router:
import ForumThread from './views/groupTitle/forumThreadSingle';
// Other routes...
let routes = [
{
path: '/groupTitle/th/:id',
component: ForumThread,
}
];
I can see in the forumthread component that $route.params.id is being passed too it; however, when I try to access it like this:
console.log('The id is: ' + $route.params.id);
It's unable to find the params portion of the object.
VueJS is pretty new to me as well as JavaScript itself. All the examples I've seen show the templates being inline with the router file which is something I am trying to prevent to help keep my code readable and clean.
What adjustments can I make so that I can pass properties to the template file?
Thanks!
If you're using the Vue Loader setup (which has <template></template> tags in the files), you need to use this to reference the $router, if you're doing so within the <script></script> part of the file.
console.log('The id is: ' + this.$route.params.id);
For and one wanting to get params in vue 3 with composition API for vue-router 4.x, it can be achieved using useRoute.
import {useRoute} from "vue-router";
setup(){
const route = useRoute();
const id = route.params.id;
}

react native use variable for image file

I know that to use a static image in react native you need to do a require to that image specifically, but I am trying to load a random image based on a number. For example I have 100 images called img1.png - img100.png in my directory. I am trying to figure out a way to do the following
<Image source={require(`./img${Math.floor(Math.random() * 100)}.png`)}/>
I know this intentionally does not work, but any workarounds would be greatly appreciated.
For anyone getting to know the react-native beast, this should help :)
I visited a couple of sites in the past too, but found it increasingly frustrating. Until I read this site here.
It's a different approach but it eventually does pay off in the end.
Basically, the best approach would be to load all your resources in one place.
Consider the following structure
app
|--img
|--image1.jpg
|--image2.jpg
|--profile
|--profile.png
|--comments.png
|--index.js
In index.js, you can do this:
const images = {
profile: {
profile: require('./profile/profile.png'),
comments: require('./profile/comments.png'),
},
image1: require('./image1.jpg'),
image2: require('./image2.jpg'),
};
export default images;
In your views, you have to import the images component like this:
import Images from './img/index';
render() {
<Image source={Images.profile.comments} />
}
Everybody has different means to an end, just pick the one that suits you best.
Da Man - Q: How is this answer using a variable?
Well, since require only accepts a literal string, you can't use variables, concatenated strings, etc. This is the next best thing. Yes, it still is a lot of work, but now you can do something resembling the OP's question:
render() {
var images = { test100: "image100" };
return (
<View>
<Text>
test {images["test" + "100"]}
</Text>
</View>
);
}
In JS require statements are resolved at bundle time (when the JS bundle is calculated). Therefore it's not supported to put variable expression as an argument for require.
In case of requiring resources it's even more trickier. When you have require('./someimage.png'), React Native packager will locale required image and it will be then bundled together with the app so that it can be used as a "static" resource when your app is running (in fact in dev mode it won't bundle the image with your app but instead the image will be served from the server, but this doesn't matter in your case).
If you want to use random image as a static resource you'd need to tell your app to bundle that image. You can do it in a few ways:
1) Add it as a static asset of your app, then reference to it with <Image src={{uri:'name_of_the_image_in_assets.png'}}/> (here is how you can add it to the native iOS app)
2) Require all the images upfront statically. Sth in a form of:
var randomImages = [
require('./image1.png'),
require('./image2.png'),
require('./image3.png'),
...
];
Then in your code you can do:
<Image src={randomImages[Math.floor(Math.random()*randomImages.length)]}/>
3) Use network image with <Image src={{uri:'http://i.imgur.com/random.jpg'}}/>
class ImageContainer extends Component {
this.state ={
image:require('default-img')
}
<View>
<Image source={this.state.image} />
</View>
}
In the context of this discussion,I had this case where wanted to dynamically assign images for a particular background. Here I change state like this
this.setState({
image:require('new-image')
})
I came to this thread looking for a way to add images in a dynamic way. I quickly found that passing in a variable to the Image -> require() was not working.
Thanks to DerpyNerd for getting me on the correct path.
After implementing the resources in one place I then found it easy to add the Images. But, I still needed a way to dynamically assign these images based on changing state in my application.
I created a function that would accept a string from a state value and would then return the Image that matched that string logically.
Setup
Image structure:
app
|--src
|--assets
|--images
|--logos
|--small_kl_logo.png
|--small_a1_logo.png
|--small_kc_logo.png
|--small_nv_logo.png
|--small_other_logo.png
|--index.js
|--SearchableList.js
In index.js, I have this:
const images = {
logos: {
kl: require('./logos/small_kl_logo.png'),
a1: require('./logos/small_a1_logo.png'),
kc: require('./logos/small_kc_logo.png'),
nv: require('./logos/small_nv_logo.png'),
other: require('./logos/small_other_logo.png'),
}
};
export default images;
In my SearchableList.js component, I then imported the Images component like this:
import Images from './assets/images';
I then created a new function imageSelect in my component:
imageSelect = network => {
if (network === null) {
return Images.logos.other;
}
const networkArray = {
'KL': Images.logos.kl,
'A1': Images.logos.a1,
'KC': Images.logos.kc,
'NV': Images.logos.nv,
'Other': Images.logos.other,
};
return networkArray[network];
};
Then in my components render function I call this new imageSelect function to dynamically assign the desired Image based on the value in the this.state.network:
render() {
<Image source={this.imageSelect(this.state.network)} />
}
Once again, thanks to DerpyNerd for getting me on the correct path. I hope this answer helps others. :)
Here is a simple and truly dynamic solution(no renaming or import required) to the problem if you have a bigger no of files.
[Won't work for Expo Managed]
Although the question is old I think this is the simpler solution and might be helpful. But I beg a pardon for any terminological mistakes, correct me please if I do any.
INSTEAD OF USING REQUIRE WE CAN USE THE URI WITH NATIVE APP ASSETS FOR ANDROID (AND/OR iOS). HERE WE WILL DISCUSS ABOUT ANDROID ONLY
URI can easily be manipulated as per the requirement but normally it's used for network/remote assets only but works for local and native assets too. Whereas require can not be used for dynamic file names and dirs
STEPS
Open android/app/src/main/assets folder from your App.js or index.js containing directory, if the assets folder doesn't exist create one.
Make a folder named images or any NAME of your choice inside assets, and paste all the images there.
Create a file named react-native.config.js in the main app folder containing App.js or index.js.
Add these lines to the new js file:
module.exports = {
project: {
ios: {},
android: {},
},
assets: ['./assets/YOUR_FOLDER_NAME/'],
};
at the place of YOUR_FOLDER_NAME use the newly created folder's name images or any given NAME
Now run npx react-native link in your terminal from main app folder, this will link/add the assets folder in the android bundle. Then rebuild the debug app.
From now on you can access all the files from inside android/app/src/main/assets in your react-native app.
For example:
<Image
style={styles.ImageStyle}
source={{ uri: 'asset:/YOUR_FOLDER_NAME/img' + Math.floor(Math.random() * 100) + '.png' }}
/>

Categories

Resources