Share data in files from a JS class - javascript

Is there a way i can use a JS Class to populate an object with data from an API and then re-use that object from the class in different files so i have access to the same data object? For example:
I have a class that fetches and stores some data.
export default class Api {
constructor() {
// Config from DOM element
this.config = JSON.parse( $( '.config' ).attr( 'data-config' ) );
// The object we send back to the API
this.payload = {
'data': {
'type': this.config.type,
'paged': 1,
'filters': []
}
};
// API response comes in here
this.response = {};
}
get() {
const self = this;
return new Promise( ( resolve ) => {
$.ajax( {
type: 'post',
contentType: 'application/json',
url: this.config.apiUrl,
data: JSON.stringify( this.payload ),
dataType: 'json',
success: ( response ) => {
self.response = response;
resolve();
}
} );
} );
}
}
file1.js:
import Api from '../helpers/api.js';
function fetchData () {
Api.get().then( () => {
// Do stuff after data fetched from class
// Api.response has data now.
} );
}
Now in file2.js i want to access the data stored in the this.response object that file1.js fetched.
file2.js
import Api from '../helpers/api.js';
console.log( Api.response ) // empty {}
I want the this.payload object in the Api Class to be updated from any file and then do a new call to the api.
I understand why it's empty because file2.js is using a new Class Api. But is there a way i can share this data without saving it in localStorage or the window object? Maybe i should not use a class for this but i'm kinda lost here. I sort of want the same thing that Vuex does but i'm not using Vue on this project.
Thanks in advance.

You're storing data in class instance, but trying to read it from class static property
If you want to you classes, you should instantiate class to get access to instance properties you've initialized in constructor. And you should use singleton pattern to get the same instance every time you call new App()
Or just export not a class, but a simple object with the same interface.

Related

load json file before app startup and save it in global variable mithrilJs

I want to load a JSON file into my mithrilJs app before its startup and want to save this data in some global variable (JSON file is for some run time configuration of mithril application just like app_initializer in Angular)
so far I have done this in my app
import m from 'mithril';
import { a } from './MainView';
var Data = {
fetch: function() {
m.request({
method: "GET",
url: "./client/config/config.json",
})
.then(function(items) {
console.log(items)
// want to store this
m.route(document.body, "/accounts", a)
})
}
}
Data.fetch()
and my Main view file contains
import m from 'mithril';
import {Layout} from "./components/layout";
import {Accounts} from "./components/accounts";
import {AccountNew} from './components/newAccount';
export const a={
"/accounts": {
render: function (vnode) {
return m(Layout, m(Accounts))
}
},
"/accountsNew": {
render: function (vnode) {
return m(Layout, m(AccountNew))
}
},
}
so what could be better approach for this and also I want to store fetched json file data (items) in some global variable like props in react or services in angular , How I can do that to access everywhere in my app
The docs state that you can use onmatch to preload data, here is a rough translation of their example:
var state = {
items: null,
loadItems: function() {
if (state.items === null) {
return m.request("./client/config/config.json").then(function(items) {
state.items = items;
});
}
}
};
const a = {
"/accounts": {
onmatch: state.loadItems,
render: function (vnode) {
return m(Layout, m(Accounts))
}
},
"/accountsNew": {
onmatch: state.loadItems,
render: function (vnode) {
return m(Layout, m(AccountNew))
}
},
}
You can read their two examples in the documentation here: Preloading data.
Alternative solutions
These solutions don't really involve mithril because your are really loading the data before mithril is even used. You should be able to pass your state variable into the component as an attribute, ie. return m(Layout, m(Accounts, {state}));
Dumping JSON String into server side template
If you control the server side as well you can just dump your configuration directly into a global variable by outputting an escaped JSON string assigned to a javascript variable in your base template. I do this to dump model information or session information so my client side code can use it.
<script> var config = ${escapedJSONStringInServerVariable};</script>
Import config directly
You can also just import the configuration directly into your app if you rewrite your config.json to just export your configuration as an object.
import {Config} from ./client/config/config.js
Call m.request directly
Finally you can also just assign the promise returned from m.request to a var and return that promise in loadItems. This should fire m.request immediately but prevent the loading of your templates until the promise is resolved.
var state = (function () {
var configRequest = m.request({
url: "./client/config/config.json"
}).then(function(items) {
state.items = items;
});
return {
items: null,
loadItems: function() {
return configRequest;
}
};
})();
Try to save it in sessionStorage and check it after every reload.
if(!sessionStorage.key('myJson'))
sessionStorage.setItem('myJson','myJson')

Use JavaScript Proxy to prepend value to argument

I'm using Axios to work with my REST API. The API serves multiple apps with routes of the form /api/<app>/....
In my "mp" app client, I'm constantly repeating the string Axios.[rest-method]('/api/mp/[...]', ...). I thought a clever way to avoid this would be to wrap Axios in a Proxy. With this proxied object, I could make my calls like Axios.mp.get(...) and it would automatically prepend /api/mp to the first argument of get().
Obviously, I could create my own wrapper object with wrapped REST methods that did this, but where's the fun in that?
Here's what I have so far:
import AxiosLib from 'axios';
const routes = {
mp: '/api/mp',
// ...
},
restMethods = new Set([
'get',
'post',
'put',
'delete',
'head',
'patch'
]);
const Axios = new Proxy(AxiosLib, {
get: function(target, prop, receiver) {
let routePrefix = routes[prop];
if (routePrefix) {
// return `receiver` with magic to store `routePrefix`
} else if (restMethods.has(prop)) {
let method = Reflect.get(target, prop, receiver);
routePrefix = /* get magically stored `routePrefix` if it exists */ || '';
return function(url, ...args) {
return method.apply(this, routePrefix + url, ...args);
}
} else {
return Reflect.get(target, prop, receiver);
}
}
});
export default Axios;
What do I put in the two commented sections?
I could create a new, second-layer Proxy for each defined route that handled this, but for cleanliness's/simplicity's sake, I'd like to use only a single proxy.

Get data from local json file in Vuejs method using jQuery $.getJSON()

I'm working on a demo app using Vuejs, and as part of it, I need to get some map data from a local .json file, get certain parts of it (such as lat/long values), and then put it into the appropriate data objects so they can be displayed on a map. After doing a lot of searching it seems that the easiest way it to use jQuery's $.getJSON() method. However, what I can't figure out is how to get the data from within the $.getJSON callback to my Vue markers object.
Here's my relevant code:
<script>
import _ from 'lodash'
import $ from 'jquery'
export default {
name: 'HelloWorld',
data () {
return {
center: {lat: 37.541885, lng: -77.440624},
markers: []
}
},
methods: {
getMarkers: function () {
var apparatus = {}
var address = []
var description = {}
$.getJSON('../static/event1.json', function (data) {
// What do I do here to get outside of this function so
// I can manipulate it?
apparatus = data.apparatus
address = data.address
description = data.description
})
}
},
created () {
this.getMarkers()
}
}
</script>
As you can see by the comment above, I need to get the data from inside the $.getJSON callback, but I can't see what I need to do that. Will this way work, or is there a better way?
I'm not sure where do you want to access this outside the function.
For example, you can assign result of $.getJSON to Vue's instance data by self.data = data, then you can access it outside of your $.getJSON
export default {
name: 'HelloWorld',
data () {
return {
center: {lat: 37.541885, lng: -77.440624},
markers: [],
data: null
}
},
methods: {
getMarkers: function () {
var apparatus = {}
var address = []
var description = {}
var self = this
$.getJSON('../static/event1.json', function (data) {
self.data = data // then you can access data outside of your function by reference this.data
// What do I do here to get outside of this function so
// I can manipulate it?
apparatus = data.apparatus
address = data.address
description = data.description
})
}
},
created () {
this.getMarkers()
}
}
I think you are looking for a way to use your data object's inside your method. You can use this to get access to your data object's :
this.markers = data;
UPDATE :
In your case, you have another scope in your method ( Your callback ) so you ned to use View Model. just before your getJSON line, define it like this :
var vm = this;
Then inside your callback, you can have access to your data object :
vm.markers = data;
notice that if you are using the older version's of vue ( below 2.x ) , you need to use it like this :
this.$data.markers = data;

ES6 Singleton vs Instantiating a Class once

I see patterns which make use of a singleton pattern using ES6 classes and I am wondering why I would use them as opposed to just instantiating the class at the bottom of the file and exporting the instance. Is there some kind of negative drawback to doing this? For example:
ES6 Exporting Instance:
import Constants from '../constants';
class _API {
constructor() {
this.url = Constants.API_URL;
}
getCities() {
return fetch(this.url, { method: 'get' })
.then(response => response.json());
}
}
const API = new _API();
export default API;
Usage:
import API from './services/api-service'
What is the difference from using the following Singleton pattern? Are there any reasons for using one from the other? Im actually more curious to know if the first example I gave can have issues that I am not aware of.
Singleton Pattern:
import Constants from '../constants';
let instance = null;
class API {
constructor() {
if(!instance){
instance = this;
}
this.url = Constants.API_URL;
return instance;
}
getCities() {
return fetch(this.url, { method: 'get' })
.then(response => response.json());
}
}
export default API;
Usage:
import API from './services/api-service';
let api = new API()
I would recommend neither. This is totally overcomplicated. If you only need one object, do not use the class syntax! Just go for
import Constants from '../constants';
export default {
url: Constants.API_URL,
getCities() {
return fetch(this.url, { method: 'get' }).then(response => response.json());
}
};
import API from './services/api-service'
or even simpler
import Constants from '../constants';
export const url = Constants.API_URL;
export function getCities() {
return fetch(url, { method: 'get' }).then(response => response.json());
}
import * as API from './services/api-service'
The difference is if you want to test things.
Say you have api.spec.js test file. And that your API thingy has one dependency, like those Constants.
Specifically, constructor in both your versions takes one parameter, your Constants import.
So your constructor looks like this:
class API {
constructor(constants) {
this.API_URL = constants.API_URL;
}
...
}
// single-instance method first
import API from './api';
describe('Single Instance', () => {
it('should take Constants as parameter', () => {
const mockConstants = {
API_URL: "fake_url"
}
const api = new API(mockConstants); // all good, you provided mock here.
});
});
Now, with exporting instance, there's no mocking.
import API from './api';
describe('Singleton', () => {
it('should let us mock the constants somehow', () => {
const mockConstants = {
API_URL: "fake_url"
}
// erm... now what?
});
});
With instantiated object exported, you can't (easily and sanely) change its behavior.
Both are different ways.
Exporting a class like as below
const APIobj = new _API();
export default APIobj; //shortcut=> export new _API()
and then importing like as below in multiple files would point to same instance and a way of creating Singleton pattern.
import APIobj from './services/api-service'
Whereas the other way of exporting the class directly is not singleton as in the file where we are importing we need to new up the class and this will create a separate instance for each newing up
Exporting class only:
export default API;
Importing class and newing up
import API from './services/api-service';
let api = new API()
Another reason to use Singleton Pattern is in some frameworks (like Polymer 1.0) you can't use export syntax.
That's why second option (Singleton pattern) is more useful, for me.
Hope it helps.
Maybe I'm late, because this question is written in 2018, but it still appear in the top of result page when search for js singleton class and I think that it still not have the right answer even if the others ways works. but don't create a class instance.
And this is my way to create a JS singleton class:
class TestClass {
static getInstance(dev = true) {
if (!TestClass.instance) {
console.log('Creating new instance');
Object.defineProperty(TestClass, 'instance', {
value: new TestClass(dev),
writable : false,
enumerable : true,
configurable : false
});
} else {
console.log('Instance already exist');
}
return TestClass.instance;
}
random;
constructor() {
this.random = Math.floor(Math.random() * 99999);
}
}
const instance1 = TestClass.getInstance();
console.log(`The value of random var of instance1 is: ${instance1.random}`);
const instance2 = TestClass.getInstance();
console.log(`The value of random var of instance2 is: ${instance2.random}`);
And this is the result of execution of this code.
Creating new instance
The value of random var of instance1 is: 14929
Instance already exist
The value of random var of instance2 is: 14929
Hope this can help someone

In Redux, what is the best way to post data to a server?

I want to post data to a server.. my action is like this:
export function addNewTodo(text) {
return {
type: 'ADD_NEW_TODO',
payload: addNewTodoApi(text)
};
}
let addNewTodoApi = function(text) {
return new Promise(function(resolve, reject) {
//implement fake method for get data from server
setTimeout(function () {
resolve({
text: text,
done: ??,
assignTo: ??,
Project: ??,
Category: ??,
id: ??
});
}, 10);
});
};
I have three way. The first way is import store and and call getState method in my action and the second way is dispatch action in reducer and the last way is pass all data in my action as argument. Which one is correct? I read this question and I'm worried about antipatterns.
You should consider using import { connect } from 'react-redux' and using containers.
You access your store on function mapStateToProps, you send all your data for your action from the container.
The signature for it is
const mapStateToProps = (state, ownProps) => {}
There you get the state which a part could be passed to your action.
The action perform post to your api using ajax with data you passed from state.
In your action your could consider using isomorphic-fetch.
Quick example:
import 'isomorphic-fetch'
const postData = ()=> ({
type: types.POST_YOURACTION,
payload: new Promise((resolve, reject) => {
fetch(api.postData(), {method: 'POST', }).then(response => {
resolve(response.json())
})
})
})
In case if you need to access the store directly you could use .getState(), accessing state directly could be considered an anti-pattern but it really depends on your architecture design.
I would suggest to look look at mapStateToProps.
Example of how to access your store.
import store from 'app/store'
store.getState().yourReducer.data
I always use the third way, pass data to the action. The data can be the data in store.getState() or from params or whatever you need to get data from server.

Categories

Resources