Share properties of multiple objects to a single one - javascript

Trying to find the way how to share properties of multiple objects to a single object.
For example: I have the app object and the homepage object. The idea is very simple, I'd like to share the properties of the homepage object with the app object and be able to do something like this:
const app = {...}
const homepage = {
clickOnSignIn() {...}
}
app.clickOnSignIn()
I know that I can simply merge this two objects, but this is not a solution for me. The plan is to add more pages in the future and still be able to use the dot notion on the app object.
Update:
I discovered another possible solution for this problem, but I'm not sure if it's the correct way of doing things.
Created the Homepage object with some methods on this object inside /pages/homepage.js file.
export const HomePage = {
clickOnSignUpButton() {},
clickOnSignInButton() {},
};
There's one more object and I called it the LoginPage.
export const LoginPage = {
fillUpSignInForm() {},
clickForgotPassword() {},
};
I exported these files inside /pages/index.js
export * from "./home.js";
export * from "./login.js";
Then I simply imported these pages as a namespace inside my app.js file and created a few getter functions.
import * as pages from "./pages/index.js";
export class App {
constructor(client) {
if (!App.instance) {
this.client = client;
App.instance = this;
}
return App.instance;
}
static get pages() {
return pages;
}
static getPage(pageName) {
return pages[pageName];
}
}
First of all, it works as excepted. Using this approach, I have access to all my pages thru my App class and now, I can do something like this:
const app = new App("some-client");
const { HomePage } = app.pages;
HomePage.clickOnSignUpButton();

Related

Updating my imported variable's value in React.js

I am refractoring an app I've build using React.js. I am exporting a variable from the global scope of Spotify.js and importing it in two other files App.js and Button.js.
After calling a function from Spotify.js that sotres a new value to the variable, It's new value is exported to 'Button.js' but stays an empty string in 'App.js'.
Your help would be appriciated :)
export let userAccessToken = '';
export const Spotify = {
...
getUserAccessToken (){
//stores a new string to userAccessToken.
}
}
import {userAccessToken, Spotify} from '../../util/Spotify';
export class App extends React.Component {
//a conditional rendering happens depending on if(!userAccessToken)
}
import {userAccessToken, Spotify} from '../../util/Spotify'
export class Button extends React.Component {
componentDidMount() {
if (!userAccessToken) {
console.log(`Button's UAT before calling the fn: ${userAccessToken}`)
Spotify.getUserAccessToken();
console.log(`Button's UAT after calling the fn: ${userAccessToken}`);
}
}
...
}
This is not how you share data between react components.
Use react context or pass props between components
You could use react context to share data or simply pass it as props (if the components are closely related in the component tree)
The only thing I can recommend is to export the userAccessToken, something like this, but you can't change its value outside the module
export const Spotify = {
...
getUserAccessToken (){
//stores a new string to userAccessToken.
}
}
...
}
const accessToken = Spotify.getUserAccessToken();
export const userAccessToken = accessToken;
If you got to read this question I solved it.
Turns out I should have called my method Spotify.getUserAccessToken() from App.js using the react lifecycle method componentDidMount().
The export and import methods are like snapshots of the module and therefore when I exported the variable userAccessToke from Spotify.js before calling the my method I imported an empty string and it did not update in all files.
Thanks Jørgen and joseluismurillorios for your support and time spent answering :)

Angular/TypeScript: How to get a reference to a class from a string at runtime without importing

I'm trying to figure out how to get a reference to a class in Angular 2+ (Angular 5) from a string at runtime. I tried the examples on this page. This one didn't work for me:
console.log((<any>Object).create(window[className])); // undefined
And the others are using an import, which I'm trying to avoid.
I'm not using a namespace, but don't know if Angular has one of its own. I tried snooping on the window object to see if I could find anything. All I found were getAllAngularRootElements, getAllAngularTestabilities, and getAngularTestability, but those didn't seem like what I was looking for.
I had a similar need once for dynamically rendering components and only having a string reference to the class that needed to be injected into the page (dynamic dashboard type of app). I ended up doing the following:
Create service to hold onto reference of component by string name
Inject service into module component was part of and register the component
Inject the service into the component that needed to get the component by string name
This was roughly what I had for the service (the class took care of actually creating the dynamic component instead of getting the reference like below):
export class DynamicComponentService {
private dynamicComponentTypes: { [type: string]: Type<BaseInterfaceSectionComponent> } = {};
registerDynamicComponentTypes(...dynamicComponentTypesToRegister: { component: Type<BaseInterfaceSectionComponent>, name: string }[]) {
dynamicComponentTypesToRegister.forEach(dynamicComponentType => {
this.dynamicComponentTypes[dynamicComponentType.name] = dynamicComponentType.component;
});
}
getDynamicComponentType(name: string): Type<BaseInterfaceSectionComponent> {
return this.dynamicComponentTypes[name];
}
}
I was unaware until doing this, but you can actually inject dependencies into a module's constructor. I used this feature to use the service to register the dynamic components:
export class BarChartContentModule {
constructor(dynamicComponentService: DynamicComponentService) {
const dynamicComponent = { component: BarChartContentComponent, name: 'BarChartContentComponent' };
dynamicComponentService.registerDynamicComponentTypes(dynamicComponent);
}
}
Not sure if this is what you were looking for, but figured I'd share.

Write global functions to use in all components in angular

Note : Its not for global variable but for a global common function to perform a functionality on all components
I am working on an angular app where I have around 400 components in different modules, almost all components have one same kind of functionality as mentioned below
There is a sections on many pages which shows a "How to work section" which can be closed by users and will remain closed unless they open it again, I have done it with cookies which I set on click on close or open icon but this function is written in a component and this needs to be imported in other components
I want to create a functions somewhere which perform this functionality on click on icon and can be called without importing any component in others.
One way to do it ( as I thought ) could be create a JavaScript function in a file and load it in index file and then call this function on click on close and open icon
Not sure if this is the best way to do this. Hope someone will come up with a better solution.
1. create your global function service, i.e. 'funcs.services.ts' under 'services' directory:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class FuncsService {
constructor() { }
myGlobalAddFunction(a){
return a++;
}
mySecondFunc(){
// add more... and so on
}
}
2. Import the function in your component:
// your path may different
import { FuncsService } from './../services/funcs/funcs.service';
//...
constructor(
private funcs: FuncsService
) {}
ngOnInit(): void {
let x = 1;
myResult = this.funcs.myGlobalAddFunction(x);
// Then you are expecting 2 for return value
}
3. Hope that works... :)
You can export a function that is a written in .ts file and then call it in all your components.
export function myFunction(){
// Do something
}
And then import the function myFunction() in other components. That works fine for me and can be useful in certain cases
This isn't the best solution (in my opinion). The best solution would be to either create a service, or an utils class.
But if you want to do this, I suggest you make a JS file, that you declare in your angular-cli.json file under the scripts property, containing your functions.
EDIT Now that you've came back to reason, here is a code sample to make utils classes.
export const IMG_UTILS = {
convertPngToJpg = (picture: any) => {
// Your logic here
}
};
export const VIEW_MANAGER = {
isAdblockActive = () => {
// test if an ad-blocker is active
}
};
You can make any utils class you want in a const, then put it into a file. Then, you can put this file in an utils folder, and request it with
import { VIEW_MANAGER } from 'src/app/utils/view-manager';
Otherwise, you can make a service, which is handled by Angular, with a console command such as
ng g s services/view-manager/view-manager
And it will behave the exact same way : you will import it in your components to use it.
Hope this helps !
The most recommended way is to use a service and inject it whenever needed, but there is a way to have a function available globally.
Although I don't think it's a really good idea, you can add the function in the index.html file, then whenever you want to use it, you have to use #ts-ignore to avoid an error from being thrown.
e.g
index.html
<script>
function globalFunc(){
alert(2)
}
</script>
anywhere else on the app
// #ts-ignore
globalFunc();
List item
Just to chime in with possibly a duplicate answer albeit more fleshed out... I have a utilities class which I use.
For example:
export class Utilities {
// Simple promise wrapper for setTimeout. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#creating_a_promise_around_an_old_callback_api
public static Wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
}
The class is referenced in a component via the import statement:
import { Utilities } from 'path/to/Utilities';
And then you can call your static methods thus:
Utilities.Wait(30000)
.then(() => DoStuff())
.catch(() => console.log('Darn!'));
I would tend to use RxJs but I've written it this way to keep things a little cleaner.

How to call a class methods in JavaScript

I am currently learning ES6. While I was playing with the new features, I got stuck at one point. How to call a class method.
So let's say I have a class in one file like below:
class Auth {
checkUserSignedIn() {
//check user signed in
}
signupUser(account) {
//signup user
}
loginUser(account) {
//login user
}
getCurentUser() {
//Current User
}
}
module.exports = Auth;
and then in some other file, let's say a controller I would like to call these functions.
const Auth = require('./auth');
class Controller {
signupUserUsingEmailAndPass(user) {
Auth.signupUser(account);
}
loginUserUsingEmailAndPass(account) {
Auth.loginUser(account);
}
isUserSignedIn() {
checkUserSignedIn();
}
}
module.exports = Controller;
But this doesn't work at all. I guess there is something I am not understanding correctly. Any suggestion/advice?
Methods defined in a class require an instance of that class, i.e. a new Auth somewhere.
Your controller should be defined has
class Controller {
constructor(auth) {
this.auth = auth;
}
}
This approach over the require('myclass') allows for you to inject different implementations of your Auth class.
Should you not desire an instance of Auth for those methods, declare them as static. More on that here
You need to either instatiate the Auth
const authorization = new Auth();
Possibly inside the file containing the Auth class, and just export the instance.
export const authorization = new Auth();
Or, if You want this methods available outside. You can make the methods static. Just add static keyword before the method name during method creation.
You can read more about static methods here
Javascript classes don't work quite like the module pattern (that it seems) you're used to. When you export `Auth, you're exporting the class definition but you still need to instantiate it.
let auth = new Auth()
auth.signupUserUsingEmailAndPass() // or any other function you define
In javascript, a class is useful when you want to populate it with data or some sort of state. If you don't want or need that then you can use a module.

MarionetteJS: who is responsible for showing sub-applications?

I am building a quite simple Marionette application; I am not using Marionette.Module since it's deprecated and since I want to use ES2015 with Webpack.
I have just a couple of "pages": one is the Welcome screen and the other one is the Playground. Each one of those pages are Applications, plus there is a root application with a Layout with just three regions: header, main and footer.
The view (or layout) of those applications are meant to fill the main region of the root layout.
Now, whenever I want to show one of those sub-applications, I don't know exactly how (or, I am not finding the most satisfying way) to ask the Layout of the root application to showChildView of the view/layout of those sub-apps.
So far, I came up with two approaches (none of which is fantastic):
EDIT: I added another approach at the end of the Q
on the controller of each sub-application, trigger the event "application:show" passing in the view. The root application is listening to this event and showChildView as soon as it receives the message
whenever I start a sub-application, I pass it the root application instance. Whenever the sub-application needs to show itself, it will call the showChildView inside the main region
The first approach is weak, because it's totally asynchronous (fire and forget). I'd like to know when my application is shown (attached to the DOM) but relying again on another event seems cumbersome
The second approach is more robust but it sucks, because of course I don't want the sub-application's views to be responsible of their own rendering in the root layout. The root application knows better.
Some code follows to try to show both ideas:
// Approach #1
// In the root app
welcomeApp.on('app:show', (view) => {
rootApp.rootView.showChildView('main', view);
});
// In the Welcome sub-app
var Controller = {
show() {
app.trigger('app:show', new WelcomeView());
}
};
// Approach #2
// In the root application
const welcomeApp = new WelcomeApp({
rootApp: this
});
// In the Welcome sub-app
var Controller = {
show() {
app.rootApp.rootLayout.showChildView('main', new WelcomeView());
}
};
EDIT: 12 Jan.
OK, digging a bit more in the documentation I found what I think is the correct way to do that. The idea is that the root Application will listen for Commands from the subapplications.
So, in my root view I will have:
this.channel.commands.setHandler("application:show", function(view) {
this.rootView.showChildView('main', view);
}.bind(this));
In all the other subapplications, I will have (for example, in the WelcomeView):
getController() {
const greet = function () {
this.channel.commands.execute('application:show', new WelcomeView());
}.bind(this);
return {
greet: greet
};
}
Personally I wouldn't use multiple applications, this seems to be just getting around the issue of modules being removed. Have you tried using a single application with a LayoutView, a Region for each component or (module) and the base of the component either being a singleton JS object, or you could try Backbone.Service to split it up. If you are using multiple routes, you could have a look at Backbone.Routing, with each Route being the base 'controller' for the 'page'.
I find a brilliant architecture for large scale Marionette applications is James Kyle's Marionette Wires. This uses Service for reusable components, and routers/routes for different data types.
EDIT
Another way of architecting using services, but if you don't want the root application's regions show methods to be called from child components, would be to import the instantiated application into the child component and use the app.addRegions method to add regions within the child. EG
//app.js
import { Application } from 'backbone.marionette';
const App = Application.extend({
onStart() {
//doSomething();
}...
});
export const app = new App();
//submodule/service.js
import { Service } from 'backbone.service';
import { SubmoduleController } from './controller';
const SubmoduleService = Service.extend({
requests() {
show: 'show',
hide: 'hide'
},
setup(options = {}) {
this.el = options.el
},
start() {
this.controller = new SubmoduleController({
el: this.el
});
},
show() {
this.controller.show();
},
hide() {
this.controller.destroy();
}
});
export const submoduleService = new SubmoduleService();
//submodule/controller.js
import { Object as Controller, Region } from 'backbone.marionette';
import { View } from './view';
import { app } from '../app';
export const SubmoduleController = Controller.extend({
initialize(options = {}) {
this.el = options.el;
this._addRegions();
},
_addRegions() {
const region = new Region({
el: this.el
});
app.addRegions({
submoduleRegion: region
});
},
show() {
const view = new View();
app.submoduleRegion.show(view);
},
onDestroy() {
app.submoduleRegion.reset();
}
});
//main.js
import { submoduleService } from './submodule/service';
const config = {
submodule: {
el: '#submodule';
},
...
};
submoduleService.setup(config.submodule);
submoduleService.show();
A gotcha with doing it this way is the submodule's region needs it's element to exist in the DOM when it's region's show method is called. This can either be achieved by having a root view on the application, whose template contains all component root elems, or to have a root submodule - page or something.

Categories

Resources