Using nested classes to simulate namespaces in ES6 - javascript

I have a file, Services.js which I am trying to load all of my individual services in. These are exposed as singletons.
Services.js
var Services = { };
export default Services;
I then want Sample Service nested under Services, so I could invoke for example Services.Sample.Operation()
`SampleService.js'
import Services from './Services';
Services.Sample = {
Operation: function() {
alert('operation!')
}
};
export default Services.Sample;
Then, I try and import:
import Services from './services/Services';
import SampleService from './services/SampleService';
alert(Services); // yields '[object object]'
alert(SampleService); // yields '[object object]'
alert(Services.Sample); // yields 'undefined' <--- This is the one I actually want to use
How can I get it so I can refer to Services.Sample rather tan SampleService. How can I make SampleService become nested under Services?

Your way doesn't work because you're importing Services.js in SampleService.js, but the Services variable is not the 'original' Services variable from Services.js.
What I'd do is something like this:
SampleService.js:
SampleService = {
Operation: function() {
alert('operation!')
}
};
export default SampleService;
Services.js:
import SampleService from './SampleService';
var Services = { };
Services.Sample = SampleService;
export default Services;
and then:
import Services from './services/Services';
alert(Services);
alert(Services.Sample);
export default Services;
This does also seem to make more sense to me regarding basic (in)dependencies and consistency of your modules (define what Services can do in Services.js not SampleService.js, SampleService could be independent of Services, the module loading Services.js should not depend on SampleService.js as well as that might change later, ...).

Related

How to initialize a shared javascript module default export

I want to share an api instance across multiple modules and be able to initialize it with external configuration. My code uses Webpack and Babel to transform those nice ES6 modules into something usable by browsers. I'm trying to achieve this:
// api.js
let api = null;
export default api;
export function initApi(config) {
// use config to configure the shared api instance (e.g. with api base url)
api = ...
}
// ======================
// entry.js
import { initApi } from './api';
import App from './App';
// Initialize the single shared instance before anyone has the chance to use it
const apiConfig = ...
initApi(apiConfig);
// Create the app and run it
// ======================
// App.js
// RootComponent has an import dependency chain that eventually imports DeeplyNestedComponent.js
import RootComponent from './RootComponent';
// Actual App code not important
// ======================
// DeeplyNestedComponent.js
// PROBLEM! This "assignment" to the api var happens before initApi is run!
import api from '../../../../api';
api.getUser(123); // Fails because "api" stays null forever even after the initApi() call
The "problem" occurs because ES6 modules are imported statically and import statements are hoisted. In other words, simply moving the import App from './App' line below initApi(apiConfig) doesn't make the import happen after initApi is called.
One way to solve this is to export an object from api.js (or in another globals.js file if I have multiple such shared objects with the same pattern) instead of a single variable like this:
// api.js
const api = {
api: null,
};
export default api;
export function initApi(config) {
// use config to configure the shared api instance (e.g. with api base url)
api.api = ... // <-- Notice the "api." notation
}
// ======================
// DeeplyNestedComponent.js
// api is now the object with an empty "api" property that will be created when initApi() is called
import api from '../../../../api';
api.api.getUser(123); // <-- Ugh :(
Is there a way to achieve initialization of a shared service instance elegantly when using ES6 modules?
In my case, DeeplyNestedComponent.js must still import the api instance somehow. In other words, there is unfortunately no context object passed from App all the way down to DeeplyNestedComponent.js that could give access the api instance.
The problem with your code is that
let api = null;
export default api;
does export the value null in the implicitly generated binding for the default export. However, you can also export arbitrary bindings under the name default by using the syntax
let api = null;
export { api as default };
This will work as expected. But you still need to make sure that no module accesses this export before you called initApi.

How can I export multiple object instances in TypeScript?

I am building a library in TypeScript that is using a dependency injection system (inversifyJS) to construct itself and resolve dependencies internally.
The problem I have is - I want to expose multiple instances from the injection system to the consumers of the library.
Currently what I am trying to do is:
import kernel from "./src/inversify.config";
import EntityManager from './src/manager/entityManager.service';
import StorageService from './src/storage/storage.service';
import LanguageService from './src/language/language.service';
export { kernel.get<EntityManager>(EntityManager) as EntityManagerInstance };
export { kernel.get<EntityManager>(LanguageService) as LanguageServiceInstance };
export { kernel.get<EntityManager>(StorageService) as StorageServiceInstance };
A solution that I see is possible is to use a facade to export types and access them later:
import EntityManager from './src/manager/entityManager.service';
import StorageService from './src/storage/storage.service';
import LanguageService from './src/language/language.service';
import InjectionFacade from './utils/injection.facede';
export { EntityManager, LanguageService, StorageService, InjectiorFacade };
// Usage:
// import {InjectionFacade, StorageService} from 'entity-manager';
// let injectionFacade: InjectionFacade = InjectionFacade.createAndResolve();
// let storageService: StorageService = injectionFacade.getStorageService();
But the problem with this is I have one more useless abstraction
Is there a way to implement this kind of solution without loosing type definitions and exporting ready-to-use objects?
You'd want to use a multiple-declaration variable, e.g.
export const
EntityManagerInstance = kernel.get<EntityManager>(EntityManager),
LanguageServiceInstance = kernel.get<EntityManager>(LanguageService),
StorageServiceInstance = kernel.get<EntityManager>(StorageService);

Ember access variable inside Mixin

import Ember from 'ember';
import myConst from '../utils/constants';
export default Ember.Mixin.create(myConst, {
getFieldId: function(productCode) {
console.log(myConst.MY_METHODS.FIELD_ID); //Not able to access this
}
});
My constants.js looks like
var myConst = {};
myConst.MY_METHODS = {
FIELD_ID: "fieldId"
};
export default myConst;
I am unable to access myConst inside the mixin. What am I doing wrong ?
I think you're confusing a couple of concepts here. In your example, myConst is a utility module that contains some default values, right? In that case, you don't want to mix it in to your mixin (which is what you're doing with Ember.Mixin.create(myConst, {...}). You should be doing something like this:
import Ember from 'ember';
import myConst from '<app-name>/utils/constants';
export default Ember.Mixin.create({
getFieldId: function(productCode) {
console.log(myConst.MY_METHODS.FIELD_ID);
}
});
If that doesn't work, it's likely something to do with your import path. I would start debugging by just console.loging myConst to make sure you have the intended object from your module.

Angular Service in Aurelia?

I've yet to find decent documentation detailing how to migrate from Angular 1.x to Aurelia. So far, I've only seen folks detailing how the concept of an Angular directive can be remade in Aurelia using #customElement. Okay, simple enough. But these examples always, always just mock data.
That said, Angular Services are singletons that can be injected into any controller/directive/service, and typically allows for the fetching of data from a server (i.e. PersonService, OrdersService).
But how are these data services modeled in Aurelia? Is everything just a class? It seems like it.
Essentially, I'd to see some code samples, a hello-world, that effectively fetches data from a service, and provides it to a #customElement. Where do the HTTP calls go? How do we even make HTTP calls? Angular uses $http, what about Aurelia?
EDIT:
Here's a simple angular service. How would one attack this in Aurelia?
app.service('SomeDataService', function () {
return {
getMyData: function (options) {
return $.ajax(options);
}
}
});
Yep- plain ES6/ES7 classes. There's no framework intrusion in your data services.
my-data-service.js
import {HttpClient} from 'aurelia-http-client'; // or 'aurelia-fetch-client' if you want to use fetch
import {inject} from 'aurelia-framework';
#inject(HttpClient)
export class MyDataService {
constructor(http) {
this.http = http;
}
getMyData() {
return this.http.get(someUrl);
}
}
fancy-custom-element.js
import {MyDataService} from './my-data-service';
import {inject} from 'aurelia-framework';
#inject(MyDataService) // aurelia's dependency injection container will inject the same MyDataService instance into each instance of FancyCustomElement
export class FancyCustomElement {
data = null;
constructor(dataService) {
this.dataService = dataService;
}
// perhaps a button click is bound to this method:
loadTheData() {
this.dataService.getMyData()
.then(data => this.data = data);
}
}

Ember DRY pattern for reusing "Ember.computed.alias"

I have a form that transitions through several views. Currently each controller.js file has a long list of these Ember.computed.alias. How can I break that out into one file and import it into each controller?
Currently in each controller.js
entityEmail: Ember.computed.alias('controllers.checkout.entityEmail'),
entityDOB: Ember.computed.alias('controllers.checkout.entityDOB'),
entityPhone: Ember.computed.alias('controllers.checkout.entityPhone'),
entityAddress1: Ember.computed.alias('controllers.checkout.entityAddress1'),
entityAddress2: Ember.computed.alias('controllers.checkout.entityAddress2'),
entityCity: Ember.computed.alias('controllers.checkout.entityCity'),
I would like to pull all that out into a file so I can simply import some 1 liner in each controller.js
This is a classic use-case for Ember.Mixin.
You can extract all these computed props into a single mixin and extend every controller (that needs to have these props) with it.
Add the following mixin to your app
// app/mixins/entity-form.js
import Ember from 'ember';
const { Mixin, inject, computed: { alias } } = Ember;
export default Mixin.create({
checkout: inject.controller(),
entityEmail: alias('checkout.entityEmail'),
entityDOB: alias('checkout.entityDOB'),
entityPhone: alias('checkout.entityPhone'),
entityAddress1: alias('checkout.entityAddress1'),
entityAddress2: alias('checkout.entityAddress2'),
entityCity: alias('checkout.entityCity')
});
And then use it in a controller
// app/controllers/example.js
import EntityFormMixin from 'yourAppName/mixins/entity-form';
const { Controller } = Ember;
export default Controller.extend(EntityFormMixin, {
// rest of controller's props and functions
});
Note: Ember.inject API is available since Ember 1.10.0. In case you are using an older version you need to replace the inject line with: needs: ['checkout'] and prefix the aliases with "controllers." like you did in your example.

Categories