vuejs configuration: using a global variable? - javascript

This seems dumb, but I have it setup like this:
in config/index.js:
module.exports = {
API_LOCATION: 'http://localhost:8080/api/'
}
then in src/app.js I have:
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueResource from 'vue-resource';
Vue.use(VueRouter);
Vue.use(VueResource);
const App = require("./app.vue");
const home = require("./components/home.vue");
const config = require('../config');
window.config = config;
Then in src/components/home.vue, I have a script block that uses it like so:
<script>
module.exports = {
data: function() {
return {
obj: null
}
},
created: function() {
this.$http.get(config.API_LOCAITON + '/call').then(res => {
// Do some business
}, res => {
// Handle some error
});
}
}
</script>
This works but it strikes me as a bad idea to use window to handle an application configuration. What's the more canonical approach here?

Import it.
<script>
import config from "../config"
module.exports = {
data: function() {
return {
obj: null
}
},
created: function() {
this.$http.get(config.API_LOCATION + '/call').then(res => {
// Do some business
}, res => {
// Handle some error
});
}
}
</script>
Or just the location.
<script>
import { API_LOCATION } from "../config"
module.exports = {
data: function() {
return {
obj: null
}
},
created: function() {
this.$http.get(API_LOCATION + '/call').then(res => {
// Do some business
}, res => {
// Handle some error
});
}
}
</script>

PROD: config/prod.env.js append your VAR='"value"'
'use strict'
module.exports = {
NODE_ENV: '"production"',
API_LOCATION: '"https://production URL"'
}
DEV: config/dev.env.js append your VAR='"value"'
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
API_LOCATION: '"http://localhost"'
})
Your variable will available in process.env.API_LOCATION or
process.env.VAR_NAME

TL;DR; Global configuration in Vue is done using the .env and .env.production files that you create in the route folder of the app (next to package.json)
I can confirm that in Vue using the Vue CLI .env file gets loaded automatically when you run npm run serve
But keep in mind the following that I checked:
Variables in the .env files have to start with VUE_APP prefix to be automatically picked up and available as process.env.VUE_APP_MYVAR in the code
If you're defining JS objects such as 'VUE_APP_MY_OBJECT={a:100}then you'll have to parse it to JSON before using it in the codeJSON.parse(process.env.VUE_APP_MY_OBJECT)`
If you're not using Webpack you might have to fiddle a bit with it to pick these config files up. According to this answer it looks like webpack should do it automatically. Dunno

Simply set ip path(or localhost) in local storage when login successfull and get value from local storage where you need through out the project.
here how you set value in localstrage.
// Set value in IpAdress
localstorage.setItem('IpAddress','192.168.100.100:8080');
// Get value from IpAddress
localstorage.getItem('IpAddress');
in my case whole path looks like:
localstorage.getItem('IpAddress')+/api/controller/method|

Related

How to store nuxtjs dynamically generated routes in vuex store

I'm trying to leverage nuxtjs SSG capabilities by creating a static web site where the pages content and navigation are fetched from an API.
I already found my way around on how to dynamically generate the routes by defining a module where I use the generate:before hook to fetch the pages content and routes. When creating the routes I store the page content as the route payload. The following code does just that and works as intended.
modules/dynamicRoutesGenerator.js
const generator = function () {
//Before hook to generate our custom routes
this.nuxt.hook('generate:before', async (generator, generatorOptions) => {
generator.generateRoutes(await generateDynamicRoutes())
})
}
let generateDynamicRoutes = async function() {
//...
return routes
}
export default generator
Now the problem I'm facing is that I have some navigation components that need the generated routes and I was thinking to store them into the vuex store.
I tried the generate:done hook but I don't know how to get the vuex store context from there. What I ended up using was the nuxtServerInit() action because as stated in the docs:
If nuxt generate is ran, nuxtServerInit will be executed for every dynamic route generated.
This is exactly what I need so I'm trying to use it with the following code:
store/index.js
export const actions = {
nuxtServerInit (context, nuxtContext) {
context.commit("dynamicRoutes/addRoute", nuxtContext)
}
}
store/dynamicRoutes.js
export const state = () => ({
navMenuNivel0: {}
})
export const mutations = {
addRoute (state, { ssrContext }) {
//Ignore static generated routes
if (!ssrContext.payload || !ssrContext.payload.entrada) return
//If we match this condition then it's a nivel0 route
if (!ssrContext.payload.navMenuNivel0) {
console.log(JSON.stringify(state.navMenuNivel0, null, 2));
//Store nivel0 route, we could use url only but only _id is guaranteed to be unique
state.navMenuNivel0[ssrContext.payload._id] = {
url: ssrContext.url,
entrada: ssrContext.payload.entrada,
navMenuNivel1: []
}
console.log(JSON.stringify(state.navMenuNivel0, null, 2));
//Nivel1 route
} else {
//...
}
}
}
export const getters = {
navMenuNivel0: state => state.navMenuNivel0
}
The action is indeed called and I get all the expected values, however it seems like that with each call of nuxtServerInit() the store state gets reset. I printed the values in the console (because I'm not sure even if it's possible to debug this) and this is what they look like:
{}
{
"5fc2f4f15a691a0fe8d6d7e5": {
"url": "/A",
"entrada": "A",
"navMenuNivel1": []
}
}
{}
{
"5fc2f5115a691a0fe8d6d7e6": {
"url": "/B",
"entrada": "B",
"navMenuNivel1": []
}
}
I have searched all that I could on this subject and altough I didn't find an example similar to mine, I put all the pieces I could together and this was what I came up with.
My idea was to make only one request to the API (during build time), store everything in vuex then use that data in the components and pages.
Either there is a way of doing it better or I don't fully grasp the nuxtServerInit() action. I'm stuck and don't know how to solve this problem and can't see another solution.
If you made it this far thanks for your time!
I came up a with solution but I don't find it very elegant.
The idea is to store the the API requests data in a static file. Then create a plugin to have a $staticAPI object that expose the API data and some functions.
I used the build:before hook because it runs before generate:before and builder:extendPlugins which means that by the time the route generation or plugin creation happen, we already have the API data stored.
dynamicRoutesGenerator.js
const generator = function () {
//Add hook before build to create our static API files
this.nuxt.hook('build:before', async (plugins) => {
//Fetch the routes and pages from API
let navMenuRoutes = await APIService.fetchQuery(QueryService.navMenuRoutesQuery())
let pages = await APIService.fetchQuery(QueryService.paginasQuery())
//Cache the queries results into staticAPI file
APIService.saveStaticAPIData("navMenuRoutes", navMenuRoutes)
APIService.saveStaticAPIData("pages", pages)
})
//Before hook to generate our custom routes
this.nuxt.hook('generate:before', async (generator, generatorOptions) => {
console.log('generate:before')
generator.generateRoutes(await generateDynamicRoutes())
})
}
//Here I can't find a way to access via $staticAPI
let generateDynamicRoutes = async function() {
let navMenuRoutes = APIService.getStaticAPIData("navMenuRoutes")
//...
}
The plugin staticAPI.js:
import APIService from '../services/APIService'
let fetchPage = function(fetchUrl) {
return this.pages.find(p => { return p.url === fetchUrl})
}
export default async (context, inject) => {
//Get routes and files from the files
let navMenuRoutes = APIService.getStaticAPIData("navMenuRoutes")
let pages = APIService.getStaticAPIData("pages")
//Put the objects and functions in the $staticAPI property
inject ('staticAPI', { navMenuRoutes, pages, fetchPage })
}
The APIService helper to save/load data to the file:
//...
let fs = require('fs');
let saveStaticAPIData = function (fileName = 'test', fileContent = '{}') {
fs.writeFileSync("./static-api-data/" + fileName + ".json", JSON.stringify(fileContent, null, 2));
}
let getStaticAPIData = function (fileName = '{}') {
let staticData = {};
try {
staticData = require("../static-api-data/" + fileName + ".json");
} catch (ex) {}
return staticData;
}
module.exports = { fetchQuery, apiUrl, saveStaticAPIData, getStaticAPIData }
nuxt.config.js
build: {
//Enable 'fs' module
extend (config, { isDev, isClient }) {
config.node = { fs: 'empty' }
}
},
plugins: [
{ src: '~/plugins/staticAPI.js', mode: 'server' }
],
buildModules: [
'#nuxtjs/style-resources',
'#/modules/staticAPIGenerator',
'#/modules/dynamicRoutesGenerator'
]

Jest: How to mock custom module that is exported from an index.js file?

This is the structure of my project (create-react-app):
Contents of /src/api/searchAPI.js:
import client from './client';
async function searchMulti(query, options = {}) {
options.query = query;
return await client.get('/search/multi', options);
}
export default {
searchMulti
};
Contents of /src/api/index.js:
import movieAPI from './movieAPI';
import personAPI from './personAPI';
import searchAPI from './searchAPI';
import configurationAPI from './configurationAPI';
export { movieAPI, personAPI, searchAPI, configurationAPI };
QuickSearch component imports searchAPI ands uses it to fetch some data over the web.
Now, I need to test (with react-testing-library) the QuickSearch component.
So, I would like to mock the api module (exported in /src/api/index.js) in order to use a mock function instead of searchAPI.searchMulti( ).
If I put below code in /src/componentns/__tests__/QuickSearch.js, it works just fine:
...
import { searchAPI } from '../../
...
...
jest.mock('../../api', () => {
return {
searchAPI: {
searchMulti: jest.fn().mockResolvedValue({ results: [] })
}
};
});
...
it('some test', () => {
searchAPI.searchMulti.mockResolvedValueOnce({ results: [] });
const { queryByTitle, getByPlaceholderText } = renderWithRouter(
<QuickSearch />
);
const input = getByPlaceholderText(/Search for a movie or person/i);
expect(searchAPI.searchMulti).not.toHaveBeenCalled();
act(() => {
fireEvent.change(input, { target: { value: 'Aladdin' } });
});
expect(searchAPI.searchMulti).toHaveBeenCalledTimes(1);
});
My problem is that I don't want to mock api in every test file that needs it. Instead, I would like to put api in a __mocks__ folder so that other tests can use it you, too.
How can I do that?

What is the best way to import/export variables in typescript?

I'm developing test automation scripts in Cucumber + Puppeteer + Typescript. I'm facing the problem of importing variables being declared in the main module, something like index.js. First, a few words about what I want to achieve:
I'd like to run my tests by executing test-runner.ts, not by npm run cucumber because the requirements need to have more control on the flow. Draft of the test-runner.ts module looks like this:
const exec = require('child_process').exec;
const commandLineArgs = require('command-line-args');
export let launchUrl: string;
const optionDefinitions: Array<object> = [
{ name: 'country', alias: 'c' },
{ name: 'environment', alias: 'e' },
{ name: 'headless', alias: 'h' },
];
function initGlobals() {
const options = commandLineArgs(optionDefinitions);
if (options.environment === 'integration') {
launchUrl = 'https://example.url.com';
}
}
function main() {
let cucumber: any;
let cucumberHtmlReporter: any;
cucumber = exec('./node_modules/.bin/cucumber-js', (stdout: any, err: any) => {
console.log(err);
console.log(`stdout: ${stdout}`);
});
cucumber.on('exit', () => {
cucumberHtmlReporter = exec('node cucumber-html-reporter.js', (stdout: any, err: any) => {
console.log(err);
console.log(`stdout: ${stdout}`);
});
});
}
initGlobals();
main();
So, as you see it mainly parse arguments, run Cucumber and exports a variable. The variable is imported in step definition file. Theoretically, it should work fine but unfortunately, it doesn't. While importing the whole function is executed once again. That means every time when import { launchUrl } from ../test-runner is executed, a new Cucumber application is run and some kind of loop happens.
The question is: how should I export the variables to achieve my goal and avoid the situation like that?
Its best practice anyway to store your constants in a seperate config file that you can export your launchUrl from. Your test-runner.ts would than import it and mutate it as needed.
export const URL_CONFIG = { launchUrl: '' };
then in your test-runner:
import { URL_CONFIG } from './config';
URL_CONFIG.launchUrl = 'foo'; //Everywhere in the ap launchUrl is 'foo'

Using proxyquire in a browserify factor bundle

Stuck with this one.
I am using laravel elxir with tsify to generate my js. I run the typescript through factor-bundle to split common js modules into a seperate files. I don't think though that will be a problem in this case because everything is in a spec.js
spec.ts
/// <reference path="../../../typings/index.d.ts" />
import "jasmine-jquery";
// #start widgets
import "./widgets/common/widget-factory/test";
factory-widget/index.ts
export class WidgetFactory {
.... this contains a require call to browser.service which i need to mock
}
factory-widget/test.ts
...
import {WidgetFactory} from "./index";
const proxyRequire = require("proxyquire");
it("should output the factory items", ()=> {
proxyRequire('./widgets/browser.service/index',{
"#global": true,
});
}
browser-service.ts
...
export class BrowserService implements IBrowserService{
//details
}
Getting an error Uncaught TypeError: require.resolve is not a function on line 262.
Here is the code ( yeah it's over 20,000 lines ) how else are you supposed to debug this stuff . ¯_(ツ)_/¯
I've looked at Stubbing with proxyquire. I am not holding my breath getting an answer on this one.
Edit: 06-09-2016
Proxquire is needed to overide the require call in the boot method of the WidgetFactory class
In factory-widget/index.ts:
boot(output = true):any {
let required = {};
if (this._sorted.length) {
this._sorted.forEach((key)=> {
if (output) {
console.log(`${this._path}${key}/index`);
// this is where is need to overide the call to require.
required[key] = require(`${this._path}${key}/index`);
}
});
this._sorted.forEach((key)=> {
let dependencies = {},
module = this._factory[key];
if (module.hasOwnProperty(this.dependencyKey)) {
module[this.dependencyKey].map((key)=> {
dependencies[_.camelCase(key)] = this.isService(module) ? new required[key] : key;
});
}
if (this.isTag(module)) {
if (output) {
document.addEventListener("DOMContentLoaded", ()=> {
riot.mount(key, dependencies);
});
}
//console.log(key,dependencies);
}
else {
}
})
}
}
I've added a proxyquireify example to the tsify GitHub repo. It's based on the simple example in the proxyquireify README.md.
The significant parts are the re-definition of require to call proxyquire in foo-spec.ts:
const proxyquire = require('proxyquireify')(require);
require = function (name) {
const stubs = {
'./bar': {
kinder: function () { return 'schokolade'; },
wunder: function () { return 'wirklich wunderbar'; }
}
};
return proxyquire(name, stubs);
} as NodeRequire;
and the configuration of the proxyquire plugin in build.js:
browserify()
.plugin(tsify)
.plugin(proxyquire.plugin)
.require(require.resolve('./src/foo-spec.ts'), { entry: true })
.bundle()
.pipe(process.stdout);
If you build the bundle.js and run it under Node.js, you should see that the message written to the console includes strings returned by the functions in the stubbed ./bar module.

Using nconf in the browser?

I'm looking to use https://github.com/flatiron/nconf in the browser. I have tried to use it with browserify but because nconf calls fs.readdirSync when it is required to scan directories for configuration, it fails in the browser.
// config.js
'use strict';
var nconf = require('nconf'); // this triggers the fs.readdirSync
var globalConfig = { my: { global: 1 }};
nconf.use('global', { type: 'literal', store: globalConfig });
var envConfig = { my: { env: 2 }};
nconf.use('env', { type: 'literal', store: envConfig });
module.exports = nconf;
Is it possible to use some sort of browserify transform (I didn't see a way to make it force the use of BRFS in nconf) or a way to use nconf (or some other similar library) to manage client side configuration?
If not nconf itself, then just something that would allow me to do something similar to this:
config.load('user-settings', { my : { user: 1 } });
config.load('global-settings', { my: { global: 2 } } );
config.get('my.user'); // 1
config.get('my.global'); // 2
config.unload('global-settings');
config.get('my.global'); // undefined
I recently had this issue myself. I decided to just put together my own config file, rather than pull in another library. here's what I ended up with:
/*
This file determines which environment we're in, and provides the app with the appropriate config
*/
export const defaults = {
searchURI: 'http://www.google.com/',
anotherURI: 'https://stackoverflow.com/',
};
export const dev = {
searchURI: 'https://www.bing.com/',
};
export const test = {
searchURI: 'https://www.yahoo.com/',
};
export const prod = {
searchURI: 'https://duckduckgo.com/',
};
const config = () => {
switch (process.env.YOUR_ENV_NAME) {
case 'dev':
return dev;
case 'test':
return test;
default:
return prod;
}
};
export default {
...defaults,
...config(),
};
using this pattern, you can import config just like you would nconf:
import config from './path/to/above/file.js';
const { searchURI, anotherURI } = config;
// OR
const searchURI = config.searchURI;
const anotherURI = config.anotherURI;

Categories

Resources