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.
Related
I am pretty new to JavaScript, coming from a Java background. I was just playing around with the NodeJS ("type": "module") Express framework but got between two types of ways for writing the methods in JS.
Below are the examples (check comments inline).
Type 1:
main.js
const method1 = () => {
...
method2();
...
};
const method2 = () => {
// this is not exported, so it works as a private method and won't be accessible in other JS files
...
};
.
.
.
// likewise there can be many other methods here
export { method1 }; // export other methods as well
Then, I can use the method1 (cannot use method2 as it is not exported) in any other JS file like below:
test.js
import { method1 } from './main.js';
method1();
Type 2:
main.js
class Main {
method1() {
...
method2();
...
}
#method2() {
// this is a private method, so won't be accessible outside of this class
...
}
// likewise other methods here
}
const main = new Main();
export default main;
Then, I can use this class instance in any other JS file like below:
test.js
import main from './main.js';
main.method1();
I want to know what is the difference between these two, when to use which, and which is better.
Both approaches work fine, but Type 2 is somewhat weird because you're using a class only to keep something from being exported.
Classes are usually used to group together data (properties on the instance of the class) with methods that operate on that data. For a silly example:
class NumberMultiplier {
constructor(num) {
this.num = num;
}
multiply(mult) {
return this.num * mult;
}
}
const n = new NumberMultiplier(5);
console.log(n.multiply(10));
Above, there is data (this.num), and there's also a method that operates on the data (multiply).
But in your case, you don't look to have instance data - you only want to group functions together, there's not really a reason to use a class. You can consider defining the functions individually - as you did in the first snippet - or you could use a plain object that gets exported, with only the properties you need:
const method2 = () => {
};
export default {
method1() {
method2();
}
};
If you do have persistent data and want to put it on the class instance, using a class and # private methods is a possibility (creating a single instance with new and then exporting that instance is an example of a singleton).
A potential issue to be aware of is that if you use export default with an object, there's no way to extract a single property of the export when importing in a single line. That is, if you only have a default export, you can't do something like
import { method1 } from './main.js'.default;
You could only do
import theObj from './main.js';
const { method1 } = theObj;
which some would consider to look a bit ugly. Having independent named exports can make it a bit easier for the consumers of a module to import only what they need into standalone identifiers, in a single line.
Classes in JS, unlike your familiarity in Java, are rarely used when not explicitly necessary. Nevertheless, there are situations where OOP in JS could be very useful.
Basically, the first method (Type 1) is what you're going to be using/seeing 99% of the time if you're doing just general JS programming such as front-end websites or apps.
If you're i.e. making a game however, you could use OOP to have better control over the different characters/object in your game.
In terms of back-end or on an infrastructural level, it really depends. You could perfectly use classes (Type 2) if you're following the MVC design pattern, but is again optional.
In the end, it comes down to your own design choice(s). You could go for FP (T1) or OOP (T2) whenever you like in JS, although there are some 'industry standards' for certain scenarios to decide when to use which.
It actually depends on what you are exporting. The type 1 is more appropriate if you export only one or a few objects. With type 1, you can export any primitive type variables or objects and can be used straightaway in the main.js.
However, if you want to export many objects and/or variables, then type 2 makes sense. With type 2, all exports are stored in an object, so you have to access them using this object.
Performance-wise both are same.
I do know that there is commands.js where functions can be added that we often use but isnt it gets way to messy if we just have bunch of functions in that js file?
Lets say i have a login form and create account form, for both of them i create page objects to have all the functions there and both of those have email input field and password input field, then i obviously do not want to have two functions in both classes :
class CreateAccPage {
private enterEmail = (email: string) => {
return this;
};
private enterPassword = (password: string) => {
return this;
};
}
class LoginPage {
private enterEmail = (email: string) => {
return this;
};
private enterPassword = (password: string) => {
return this;
};
}
So my concern is having lots of functions in a single commands class which isnt nice in my opinion so what could i do ? creating parent Form class and having those functions there then inherit them or there is a better way. Thank you
You can have custom commands splitted between several independent JavaScript files, without need of class inheritance. Just remember to import those JavaScript files in Cypress's supportFile.
Cypress docs clearly states that the common functions defined in cypress/support/commands.js file can be originated something else (very similar to express.js router)
Cypress.Commands.add(name, callbackFn) - cypress
app.get('/', callbackFn) - express.js
In both use cases, you can choose to use inline implementation (as you noted - it will get very messy pretty quickly) or define your functions elsewhere and then use them as delegates. To simplify it - instead of doing this:
Cypress.Commands.add('visit page', (path) => cy.visit(path)
Do this:
/-- Http helpers file: --/
class Http {
static visit(path) {
cy.visit(path)
}
}
/-- commands.js file: --/
Cypress.Commands.add('visit page', Http.visit) // delegation to your function
This way you'll be able to use function from within classes (or without - whatever suits you) and maintain your code properly as a function of time
I have the following two components:
// component.js
// imports ...
function ListItem(item) {
const html = wire(item)
function render() {
return html`<li>${item.foo}</li>`
}
return render()
}
function List(items) {
const html = wire(items)
function render() {
return html`<ul>${items.map(ListItem)}</ul>`
}
return render()
}
I want to put them in a module which is shared between the client and the server. However, as far as I can tell, although the API pretty much identical, on the server I have to import the functions from the viperHTML module, on the client I have to use the hyperHTML module. Therefore I can not just import the functions at the top of my shared module, but have to pass to my components at the call site.
Doing so my isomorphic component would look like this:
// component.js
function ListItem(html, item) {
//const html = wire(item) // <- NOTE
function render() {
return html`<li>${item.foo}</li>`
}
return render()
}
function List(html, itemHtmls /* :( tried to be consistent */, items) {
//const html = wire(items) // <- NOTE
function render() {
return html`<ul>${items.map(function(item, idx) {
return ListItem(itemHtmls[idx], item)
})}</ul>`
}
return render()
}
Calling the components from the server:
// server.js
const {hyper, wire, bind, Component} = require('viperhtml')
const items = [{foo: 'bar'}, {foo: 'baz'}, {foo: 'xyz'}]
// wire for the list
const listWire = wire(items)
// wires for the children
const listItemWires = items.map(wire)
const renderedMarkup = List(listWire, listItemWires, items)
Calling from the browser would be the exact same, expect the way hyperhtml is imported:
// client.js
import {hyper, wire, bind, Component} from 'hyperhtml/esm'
However it feels unpleasant to write code like this, because I have a feeling that the result of the wire() calls should live inside the component instances. Is there a better way to write isomorphic hyperHTML/viperHTML components?
update there is now a workaround provided by the hypermorphic module.
The ideal case scenario is that you have as dependency only viperhtml, which in turns brings in hyperhtml automatically, as you can see by the index.js file.
At that point, the client bundler should, if capable, tree shake unused code for you but you have a very good point that's not immediately clear.
I am also not fully sure if bundlers can be that smart, assuming that a check like typeof document === "object" would always be true and target browsers only.
One way to try that, is to
import {hyper, wire, bind, Component} from 'viperhtml'
on the client side too, hoping it won't bring in viperHTML dependencies once bundled 'cause there's a lot you'd never need on the browser.
I have a feeling that the result of the wire() calls should live
inside the component instances.
You could simplify your components using viper.Component so that you'll have render() { return this.html... } and you forget about passing the wire around but I agree with you there's room for improvements.
At that point you only have to resolve which Component to import in one place and define portable components that work on b both client and server.
This is basically the reason light Component exists in the first place, it give you the freedom to focus on the component without thinking about what to wire, how and/or where (if client/server).
~~I was going to show you an example but the fact you relate content to the item (rightly) made me think current Component could also be improved so I've created a ticket as follow up for your case and I hope I'll have better examples (for components) sooner than later.~~
edit
I have updated the library to let you create components able to use/receive data/items as they're created, with a code pen example.
class ListItem extends Component {
constructor(item) {
super().item = item;
}
render() {
return this.html`<li>${this.item.foo}</li>`;
}
}
class List extends Component {
constructor(items) {
super().items = items;
}
render() {
return this.html`
<ul>${this.items.map(item => ListItem.for(item))}</ul>`;
}
}
When you use components you are ensuring yourself these are portable across client/server.
The only issue at this point would be to find out which is the best way to retrieve that Component class.
One possible solution is to centralize in a single entry point the export of such class.
However, the elephant in the room is that NodeJS is not compatible yet with ESM modules and browsers are not compatible with CommonJS so I don't have the best answer because I don't know if/how you are bundling your code.
Ideally, you would use CommonJS which works out of the box in NodeJS and is compatible with every browser bundler, and yet you need to differentiate, per build, the file that would export that Component or any other hyper/viperHTML related utilities.
I hope I've gave you enough hints to eventually work around current limitations.
Apologies if for now I don't have a better answer. The way I've done it previously used external renders but it's quite possibly not the most convenient way to go with more complex structures / components.
P.S. you could write those functions just like this
function ListItem(item) {
return wire(item)`<li>${item.foo}</li>`;
}
function List(items) {
return wire(items)`<ul>${items.map(ListItem)}</ul>`;
}
Inside Angular service i have a variable count, which i want to observe whenever it's updated, i want to set that updated value to another variable in a different component.
import {BehaviorSubject} from "rxjs/BehaviorSubject";
import 'rxjs/Rx';
export class DashboardService{
count = new BehaviorSubject<number>(0);
count$=this.count.asObservable();
addCount(data){
this.count.next(data);
}
}
I get the error eventhough i have imported all the relevant libraries .Any idea why i'm getting this? can anyone tell me what's happening with the code ?
}
rxJs version 5.4.3 latest!
Try this:
export class DashboardService {
count = new BehaviorSubject<number>(0);
count$ = this.count.asObservable();
public addCount = (data) => {
this.count.next(data);
}
}
The issue you are encountering is that you are using addCount in a context where this inside the function is altered (i.e. pointing to something else). Using lamda based functions in typescript ensures this can not happen. This is not an excuse for always using them however.
I recommend you read up a bit on lambda/arrow functions, it might also be a good idea to look in to how this works in javascript and in typescript.
I am currently working on porting a Backbone project to an Angular 2 project (obviously with a lot of changes), and one of the project requirements requires certain methods to be accessible publicly.
A quick example:
Component
#component({...})
class MyTest {
private text:string = '';
public setText(text:string) {
this.text = text;
}
}
Obviously, I could have <button (click)="setText('hello world')>Click me!</button>, and I would want to do that as well. However, I'd like to be able to access it publicly.
Like this
<button onclick="angular.MyTest.setText('Hello from outside angular!')"></click>
Or this
// in the js console
angular.MyTest.setText('Hello from outside angular!');
Either way, I would like the method to be publicly exposed so it can be called from outside the angular 2 app.
This is something we've done in backbone, but I guess my Google foo isn't strong enough to find a good solution for this using angular.
We would prefer to only expose some methods and have a list of public apis, so if you have tips for doing that as well, it'd be an added bonus. (I have ideas, but others are welcomed.)
Just make the component register itself in a global map and you can access it from there.
Use either the constructor or ngOnInit() or any of the other lifecycle hooks to register the component and ngOnDestroy() to unregister it.
When you call Angular methods from outside Angular, Angular doesn't recognize model change. This is what Angulars NgZone is for.
To get a reference to Angular zone just inject it to the constructor
constructor(zone:NgZone) {
}
You can either make zone itself available in a global object as well or just execute the code inside the component within the zone.
For example
calledFromOutside(newValue:String) {
this.zone.run(() => {
this.value = newValue;
});
}
or use the global zone reference like
zone.run(() => { component.calledFromOutside(newValue); });
https://plnkr.co/edit/6gv2MbT4yzUhVUfv5u1b?p=preview
In the browser console you have to switch from <topframe> to plunkerPreviewTarget.... because Plunker executes the code in an iFrame. Then run
window.angularComponentRef.zone.run(() => {window.angularComponentRef.component.callFromOutside('1');})
or
window.angularComponentRef.zone.run(() => {window.angularComponentRef.componentFn('2');})
This is how i did it. My component is given below. Don't forget to import NgZone. It is the most important part here. It's NgZone that lets angular understand outside external context. Running functions via zone allows you to reenter Angular zone from a task that was executed outside of the Angular zone. We need it here since we are dealing with an outside call that's not in angular zone.
import { Component, Input , NgZone } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'example',
templateUrl: './example.html',
})
export class ExampleComponent {
public constructor(private zone: NgZone, private router: Router) {
//exposing component to the outside here
//componentFn called from outside and it in return calls callExampleFunction()
window['angularComponentReference'] = {
zone: this.zone,
componentFn: (value) => this.callExampleFunction(value),
component: this,
};
}
public callExampleFunction(value: any): any {
console.log('this works perfect');
}
}
now lets call this from outside.in my case i wanted to reach here through the script tags of my index.html.my index.html is given below.
<script>
//my listener to outside clicks
ipc.on('send-click-to-AT', (evt, entitlement) =>
electronClick(entitlement));;
//function invoked upon the outside click event
function electronClick(entitlement){
//this is the important part.call the exposed function inside angular
//component
window.angularComponentReference.zone.run(() =
{window.angularComponentReference.componentFn(entitlement);});
}
</script>
if you just type the below in developer console and hit enter it will invoke the exposed method and 'this works perfect ' will be printed on console.
window.angularComponentReference.zone.run(() =>
{window.angularComponentReference.componentFn(1);});
entitlement is just some value that is passed here as a parameter.
I was checking the code, and I have faced that the Zone is not probably necessary.
It works well without the NgZone.
In component constructor do this:
constructor(....) {
window['fncIdentifierCompRef'] = {
component = this
};
}
And in the root script try this:
<script>
function theGlobalJavascriptFnc(value) {
try {
if (!window.fncIdentifierCompRef) {
alert('No window.fncIdentifierCompRef');
return;
}
if (!window.fncIdentifierCompRef.component) {
alert('No window.fncIdentifierCompRef.component');
return;
}
window.fncIdentifierCompRef.component.PublicCmpFunc(value);
} catch(ex) {alert('Error on Cmp.PublicCmpFunc Method Call')}
}
</script>
This works to me.
The problem is that Angular's components are transpiled into modules that aren't as easy to access as regular JavaScript code. The process of accessing a module's features depends on the module's format.
An Angular2 class can contain static members that can be defined without instantiating a new object. You might want to change your code to something like:
#component({...})
class MyTest {
private static text: string = '';
public static setText(text:string) {
this.text = text;
}
}
Super simple solution!! save component or function with an alias outside
declare var exposedFunction;
#Component({
templateUrl: 'app.html'
})
export class MyApp {
constructor(public service:MyService){
exposedFunction = service.myFunction;
}
at index.html add in head
<script>
var exposedFunction;
</script>
Inside exposed function do not use this. parameters if you need them you will have to use closures to get it to work
This is particularly useful in ionic to test device notifications on web instead of device