Javascript Variable Name as Json Object Name - javascript

First of all the question title doesn't make sense. Im not sure how can I ask the question in a better way.
Im facing an issue in an Angular 8.1 project. There is a josn file Im importing that in the settings Class (its a service).
When the environment.ts file have a variable as app_company. the same name a json file also available. So if the client app_company matches the json file. all the config should be loaded from that file. This is what I need to achieve
What I tried so far is .
import { Injectable } from '#angular/core';
import { environment } from '#env/environment';
import * as client1 from './client1.json';
#Injectable({
providedIn: 'root'
})
export class SettingsService {
newInstance:object;
constructor() { }
config(key : string){
// const newInstance = Object.create(window[environment.app_company].prototype);
// newInstance.constructor.apply(newInstance);
// return newInstance.key;
// const newInstance: any = new (<any>window)[environment.app_company];
// console.log(Object.create(window[environment.app_company]));
return environment.app_company.key;
}
}
my json file will look like below
{
"opNumberLabel" : "OP No"
}
So in the return section if I call client1.opNumberLabel
It works like my expectation , but I try to make that dynamic like environment.app_company.key it not working .
As you can see my tried are commented those are not worked so far :(.
Any hint will highly appreciated .
Thanks in advance,

Finally I resolved.
export class SettingsService {
clients = { client1 , client2 };
app_company :string;
default_client = 'client1';
constructor() { }
config(label : string){
this.app_company = environment.app_company;
if(this.clients[this.app_company].default.hasOwnProperty(label)){
return this.clients[this.app_company].default[label];
}else{
return this.clients[this.default_client].default[label];
}
}
}
Thanks to #Titus for the hint.

Would you like to achieve something like that?
import { Injectable } from '#angular/core';
import * as settings from "./environment.json";
interface Settings {
[key: string]: string
}
type Environment = {
[key: string]: Partial<Settings>
};
#Injectable({
providedIn: 'root'
})
export class SettingsService {
private environment: Environment = {
app_company: { }
};
constructor() {
this.environment.app_company = settings;
console.log(this.config("opNumberLabel")); // OP No
}
config(key : string){
return this.environment.app_company[key];
}
}
{
"opNumberLabel" : "OP No",
"settings1": "testSettings1",
"settings2": "testSettings2"
}
If you would like to have app_company as a dynamic value too, then I would suggest to extend the example:
by creating an init function which could also accept the name of
the client.
by passing an object to the SettingsService
constructor, with InjectionToken, so you can define what is the
current client you are trying to configure.
You might also want to have different environment setup for different clients, so I would suggest to create separate environment json files for each client.
You could create an object which would hold every <client>.environment.json with <client> key.
After that you have an object, like that:
const clientEnvironments: Environment = {
client1: { ... },
client2: { ... }
};
So by having the name of your current client, you can easily select which environment you want to use.

Related

How to import class in export function?

I am trying to import class and use its public methods but its not working , what is correct way implement it.
main.ts
import {PromiseHandler } from './promiseHandler.ts';
export function getUser(req: Request, res: Response) {
const promiseHandler: new PromiseHandler();
}
promiseHandler.ts
export class PromiseHandler {
constructor() {};
public executeAllPromises(promises) {
}
As told in comments, the correct syntax is
const promiseHandler = new PromiseHandler();
(note the use of =, to assign the object created, while : would be to type the variable. The type is actually just PromiseHandler, so you could use both and write :
const promiseHandler: PromiseHandler = new PromiseHandler();
but I think that is not necessary to declare type here, TypeScript would detect it the correct type by itself with the initialisation with = new...

add data to the end of a behavior object array Angular 5

I have some data that I want to be shared with my entire app so I have created a service like so..
user.service
userDataSource = BehaviorSubject<Array<any>>([]);
userData = this.userDataSource.asObservable();
updateUserData(data) {
this.userDataSource.next(data);
}
then in my component Im getting some data from an api and then sending that data to userDataSource like so..
constructor(
private: userService: UserService,
private: api: Api
){
}
ngOnInit() {
this.api.getData()
.subscribe((data) => {
this.userService.updateUserData(data);
})
}
now that all works but.. I want to be able to add data to the end of the array inside the userDataSource so basically the equivalent of a .push am I able to just call the updateUserData() function and add more data or will doing that overwrite what is currently in there?
Any help would be appreciated
You can add a new method to your service like addData in which you can combine your previous data with new data like.
import {Injectable} from '#angular/core';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
#Injectable()
export class UserService {
userDataSource: BehaviorSubject<Array<any>> = new BehaviorSubject([]);
userData = this.userDataSource.asObservable();
updateUserData(data) {
this.userDataSource.next(data);
}
addData(dataObj) {
const currentValue = this.userDataSource.value;
const updatedValue = [...currentValue, dataObj];
this.userDataSource.next(updatedValue);
}
}
For someone that may come accross this issue with a BehaviorSubject<YourObject[]>.
I found in this article a way to properly add the new array of YourObject
import { Observable, BehaviorSubject } from 'rxjs';
import { YourObject} from './location';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class ObjService {
private theObjData: BehaviorSubject<YourObject[]> = new BehaviorSubject<YourObject[]>(null);
constructor() {
}
public SetObjData(newValue: YourObject[]): void {
this.theObjData.next(Object.assign([], newValue));
}
}
How to update data:
// inside some component
this.api.userData().subscribe((results:YourObject) =>
this.objService.SetObjData(results);
)
How to observe changes on other component
// inside another component
ngOnInit() {
this.objService.GetAccountStatements().subscribe((results) =>
...
)
}
Normally Observables and Subjects are meant to be streams of data, not an assignment of data. BehaviorSubjects are different because they hold their last emitted value.
Normally Subjects or BehaviorSubjects inside of a contained class (like a Service) do not want to expose themselves publicly to any other classes, so it's best practice to access their properties with getters or methods. This keeps the data stream cold to all subscribers.
However, since the BehaviorSubject holds the last emitted value, there's a few options here. If all subscribers need a concatenated stream of data from every emission, you could access the last emitted value and append to it:
userDataSource = BehaviorSubject<any[]>([]);
userData = this.userDataSource.asObservable();
updateUserData(data) {
this.userDataSource.next(this.userDataSource.value.push(data));
}
...or, in what might be considered better practice, Subscribers to this Subject could do their own transformation on the stream:
this.api.userData()
.scan((prev, current) => prev.push(current). [])
.subscribe((data) => {
this.concatenatedUserData = data;
});
Use concat to add object
userDataSource = BehaviorSubject<Array<any>>([]);
updateUserData(data) {
this.userDataSource.next(this.userDataSource.value.concat(data));
}
Use filter to remove object
removeUserData(data) {
this.userDataSource.next(this.userDataSource.value.filter(obj => obj !== data));
}

Post/Get data from service doesn't work like it should

To pass data around few components I created a service:
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class Services {
private messageSource = new BehaviorSubject<string>("lol");
currentMessage = this.messageSource.asObservable();
constructor() {
}
changeMessage(message: string) {
this.messageSource.next(message);
console.log(message);
}
}
I create string messagesource for contain the passing data, and current message for Observable. And after that I create function to change this data.
So now, In ngOnInit() in one of component, I write:
this.data.currentMessage.subscribe(message => this.message = message);
and define in component message:string save there message.
In function I make:
selectNews(news) {
this.data.changeMessage("lol3"); // string for test
}
And after that, I want to get this string in other component
So again in ngOnInit, I copy the same line and define the message. But the consolelog here show me the string before function make action....
So were is the problem?
Edit
Fiddle with all whole code :
https://jsfiddle.net/fywdzz51/
you are providing the service separately for each component. this creates different instances in each component. you should provide it in one place for the whole module

Angular 2 - TypeError: Cannot read property 'createMessage' of undefined

I have the following Javascript file that i would like to import into my Angular 2 project without re-writing the whole thing in Typescript.
When everything is tied up correctly i get the following error...
"EXCEPTION: TypeError: Cannot read property 'createMessage' of undefined"
Here are my corresponding files...
applicationAPI.js
var myApplication = myApplication || {};
(function(myApplication) {
myApplication.Message = function() {
this.m_messageContents = "New Message";
};
myApplication.Message.prototype.getApplicationMessageContents = function() {
return this.m_messageContents;
};
myApplication.SystemFactory = (function(){
var factory =
{
createMessage: function() {
return new myApplication.Message();
}
};
return factory;
}());
}(myApplication));
applicationAPI.d.ts
declare module "myApplicationAPI" {
export interface Message {
getApplicationMessageContents(): string;
}
export class SystemFactory {
static createMessage(): Message;
}
}
strangely I can get this working when applicationAPI.js looks like the following with the same applicationAPI.d.ts file.
applicationAPI.js
(function() {
this.Message = function() {
this.m_messageContents = "New Message";
};
this.Message.prototype.getApplicationMessageContents = function() {
return this.m_messageContents;
};
this.SystemFactory = (function(){
var factory =
{
createMessage: function() {
return new this.Message();
}
};
return factory;
}());}());
Any thoughts on what else needs to be added for this scenario to work? It is not obvious to me...
This is where the call is coming from...
home.component.ts
import { Component, OnInit } from '#angular/core';
import * as myApp from "myApplicationAPI";
#Component({
moduleId: module.id,
selector: 'app-home',
templateUrl: 'home.component.html',
styleUrls: ['home.component.css']
})
export class HomeComponent implements OnInit {
title: string;
constructor() {}
ngOnInit() {
this.title = myApp.SystemFactory.createMessage().getApplicationMessageContents();
}
}
You're not exporting anything out of applicationAPI.js so you can't import anything from it. When you import * as myApp from "myApplicationAPI"; you're not getting what you think you're getting.
Your first instinct might be to export myApplication but you should be exporting classes and interfaces, not instances. Let Angular's Dependency Injector handle instances.
I'd recommend a pattern more like this:
applicationAPI.ts
import Injectable from '#angular/core';
// either define Message in this file or import it as well
// if you define it here, export it as well
#Injectable
export class Api {
static createMessage(): Message {
return new Message();
}
}
home.component.ts
import Api from 'applicationAPI';
...
constructor (private api: Api) {...}
And assuming your bootstrap call looks something like bootstrap(HomeComponent) it should be changed to be
import Api from 'applicationAPI';
...
bootstrap(HomeComponent, [Api]);
This will insure that every time Api is injected, it's the same instance although that may not matter to you if all the methods on the class are static.
.d.ts files are used as reference by the typescript compiler saying something along the lines of "trust me, there's something that looks and acts like this" such as when you want your typescript to interface with a JS library. That library still has to be defined somewhere else outside of the .d.ts file. Nothing you put in a .d.ts file will execute or even be seen by the browser. If Message is only defined in a .d.ts file then you can't use new Message because at the end of the day when the javascript is running in the browser there won't be any definition for what a Message is.

How do I split a TypeScript class into multiple files?

I found a lot of examples and also tried myself to split a module into several files. So I get that one, very handy. But it's also practical sometimes to split a class for the same reason. Say I have a couple of methods and I don't want to cram everything into one long file.
I'm looking for something similar to the partial declaration in C#.
Lately I use this pattern:
// file class.ts
import { getValue, setValue } from "./methods";
class BigClass {
public getValue = getValue;
public setValue = setValue;
protected value = "a-value";
}
// file methods.ts
import { BigClass } from "./class";
function getValue(this: BigClass) {
return this.value;
}
function setValue(this: BigClass, value: string ) {
this.value = value;
}
This way we can put methods in a seperate file. Now there is some circular dependency thing going on here. The file class.ts imports from methods.ts and methods.ts imports from class.ts. This may seem scary, but this is not a problem. As long as the code execution is not circular everything is fine and in this case the methods.ts file is not executing any code from the class.ts file. NP!
You could also use it with a generic class like this:
class BigClass<T> {
public getValue = getValue;
public setValue = setValue;
protected value?: T;
}
function getValue<T>(this: BigClass<T>) {
return this.value;
}
function setValue<T>(this: BigClass<T>, value: T) {
this.value = value;
}
You can't.
There was a feature request to implement partial classes, first on CodePlex and later on GitHub, but on 2017-04-04 it was declared out-of-scope. A number of reasons are given, the main takeaway seems to be that they want to avoid deviating from ES6 as much as possible:
TypeScript already has too many TS-specific class features [...] Adding yet another TS-specific class feature is another straw on the camel's back that we should avoid if we can. [...] So if there's some scenario that really knocks it out of the park for adding partial classes, then that scenario ought to be able to justify itself through the TC39 process.
I use this (works in typescript 2.2.2):
class BigClass implements BigClassPartOne, BigClassPartTwo {
// only public members are accessible in the
// class parts!
constructor(public secret: string) { }
// this is ugly-ish, but it works!
methodOne = BigClassPartOne.prototype.methodOne;
methodTwo = BigClassPartTwo.prototype.methodTwo;
}
class BigClassPartOne {
methodOne(this: BigClass) {
return this.methodTwo();
}
}
class BigClassPartTwo {
methodTwo(this: BigClass) {
return this.secret;
}
}
I use plain subclassing when converting large old multi file javascript classes which use 'prototype' into multiple typescript files:
bigclassbase.ts:
class BigClassBase {
methodOne() {
return 1;
}
}
export { BigClassBase }
bigclass.ts:
import { BigClassBase } from './bigclassbase'
class BigClass extends BigClassBase {
methodTwo() {
return 2;
}
}
You can import BigClass in any other typescript file.
A modified version of proposed pattern.
// temp.ts contents
import {getValue, setValue} from "./temp2";
export class BigClass {
// #ts-ignore - to ignore TS2564: Property 'getValue' has no initializer and is not definitely assigned in the constructor.
public getValue:typeof getValue;
// #ts-ignore - to ignore TS2564: Property 'setValue' has no initializer and is not definitely assigned in the constructor.
public setValue:typeof setValue;
protected value = "a-value";
}
BigClass.prototype.getValue = getValue;
BigClass.prototype.setValue = setValue;
//======================================================================
// temp2.ts contents
import { BigClass } from "./temp";
export function getValue(this: BigClass) {
return this.value;
}
export function setValue(this: BigClass, value: string ) {
this.value = value;
}
Pros
Doesn't create additional fields in class instances so there is no overhead: in construction, destruction, no additional memory used. Field declations in typescript are only used for typings here, they don't create fields in Javascript runtime.
Intellisence is OK (tested in Webstorm)
Cons
ts-ignore is needed
Uglier syntax than #Elmer's answer
The rest properties of solutions are same.
Modules let you extend a typescript class from another file:
user.ts
export class User {
name: string;
}
import './user-talk';
user-talk.ts
import { User } from './user';
class UserTalk {
talk (this:User) {
console.log(`${this.name} says relax`);
}
}
User.prototype.sayHi = UserTalk.prototype.sayHi;
declare module './user' {
interface User extends UserTalk { }
}
Usage:
import { User } from './user';
const u = new User();
u.name = 'Frankie';
u.talk();
> Frankie says relax
If you have a lot of methods, you might try this:
// user.ts
export class User {
static extend (cls:any) {
for (const key of Object.getOwnPropertyNames(cls.prototype)) {
if (key !== 'constructor') {
this.prototype[key] = cls.prototype[key];
}
}
}
...
}
// user-talk.ts
...
User.extend(UserTalk);
Or add the subclass to the prototype chain:
...
static extend (cls:any) {
let prototype:any = this;
while (true) {
const next = prototype.prototype.__proto__;
if (next === Object.prototype) break;
prototype = next;
}
prototype.prototype.__proto__ = cls.prototype;
}
You can use multi file namespaces.
Validation.ts:
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
}
LettersOnlyValidator.ts (uses the StringValidator from above):
/// <reference path="Validation.ts" />
namespace Validation {
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string) {
return lettersRegexp.test(s);
}
}
}
Test.ts (uses both StringValidator and LettersOnlyValidator from above):
/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
// Some samples to try
let strings = ["Hello", "101"];
// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["Letters only"] = new Validation.LettersOnlyValidator();
Why not just use Function.call that js already comes with.
class-a.ts
Class ClassA {
bitten: false;
constructor() {
console.log("Bitten: ", this.bitten);
}
biteMe = () => biteMe.call(this);
}
and in other file bite-me.ts
export function biteMe(this: ClassA) {
// do some stuff
// here this refers to ClassA.prototype
this.bitten = true;
console.log("Bitten: ", this.bitten);
}
// using it
const state = new ClassA();
// Bitten: false
state.biteMe();
// Bitten: true
For more information have a look at the definition of Function.call
Personally I use #partial decorator acts as a simplified syntax that may help divide functionality of a single class into multiple 🍬🍬🍬 class files ... https://github.com/mustafah/partials
// Declaration file
class BigClass {
declare method: (n: number, s: string) => string;
}
// Implementation file
BigClass.prototype.method = function (this: BigClass, n: number, s: string) {
return '';
}
The downside of this approach is that it is possible to declare a method but to forget to actually add its implementation.
We can extend class methods gradually with prototype and Interface definition:
import login from './login';
import staffMe from './staff-me';
interface StaffAPI {
login(this: StaffAPI, args: LoginArgs): Promise<boolean>;
staffsMe(this: StaffAPI): Promise<StaffsMeResponse>;
}
class StaffAPI {
// class body
}
StaffAPI.prototype.login = login;
StaffAPI.prototype.staffsMe = staffsMe;
export default StaffAPI;
This is how i have been doing it
Mixins approach
To add to #Elmer's solution, I added following to get it to work in separate file.
some-function-service-helper.ts
import { SomeFunctionService } from "./some-function-service";
export function calculateValue1(this: SomeFunctionService) {
...
}
some-function-service.ts
import * as helper from './some-function-service-helper';
#Injectable({
providedIn: 'root'
})
export class SomeFunctionService {
calculateValue1 = helper.calculateValue1; // helper function delcaration used for getNewItem
public getNewItem() {
var one = this.calculateValue1();
}

Categories

Resources