Sharing a service between multiple Angular Modules - javascript

I have a bit of an unorthodox angular set up, and I'm starting to doubt if it's going to work.
I've split the web-app that I'm building into 5 apps. Each will have it's own module, and it's own gulp build that will compile that module into a minified js file that will be included in the html. There will be 1 js build per page.
Then, I have a sharedService module. Each of the other modules will have this sharedService module injected into it. The shared service has it's own gulp build, and is being included in the master template that every other page is inheriting from, so every page will have this shared template js build on it.
I'm finding now though that changes to the sharedService in one app, isn't reflected when I navigate to the other. I'm thinking each app is using it's own version of the sharedService and it's not actually being shared at all, or to be more specific, when navigating to the next app the page refreshes and reloads the services losing the data that existed within them.
I'm using typescript and browserify.
An example of one of my apps:
'use strict';
declare var angular, require:any;
angular.module('compareApp', ['ui.router', 'sharedServices']);
// require module dependencies
require('./compareAppConfig.ts');
require('./compareController.ts');
The shared Service app:
'use strict';
declare var angular, require:any;
angular.module('sharedServices', []);
require('./services/dogService.ts');
require('./services/userService.ts');
And an example of one of the services:
declare var require, angular:any;
angular.module('sharedServices').service('userService', userService);
const API_PATH = "/api/";
function userService($http, $q) {
var userData = {
favorites: []
};
function getUser(){
return userData;
}
function saveFavorite(dogId){
userData.favorites.push(dogId);
}
return {
saveFavorite: saveFavorite,
getFavorite: getFavorite
};
}
export = userService;
So basically when I save a dog to the favorites array in one app, and then navigate to another app, that array is empty.

When you say split the webapp do you mean separate html files?
If you are navigating to an entirely separate page/html file then your code is going to be re-run when the page loads. Therefore any data in your shared service gets wiped.
If you would like to route to different pages of your application while maintaining state I suggest looking into client-side routing using ui-router.

Related

Properties file in JavaScript / Angular

In Java I usually create application.properties in my resource folder and put configs in there.
And when I need it I just do Properties prop = new Properties(); prop.load(... my file) and then use prop.getProperty("Something")
I want to do something similar in Javascript
In my js code I have this:
// REST API Base URL
var baseUrl = "http://localhost:8083/api";
I want this to be in a application.properties file and then load the value.
How can I achive this?
Thanks
In angular 2+ projects and for a good practices you should create environments folder with one file per env like: environment.js, environment.prod.js.
and into file you can export a constant or by default like that
export const environment = {
apiUrl: '',
googleApiKey: '',
}
and you can import this environment in every file you will needed like
import { environment } from '{relativePath}/environment/environment.js'
If you create different files for every env like prod. You need to replace environment.js for env that you will be build. You have lot of info about this with webpack or other compilers.
I recommend you strongly to develop into a common.js project. It will be more friendly for you importing modules and you will have powerful possibilities of scalable app.
But the easy(Ugly) solution is:
index.html
<head>
<script src="environment.js">
<script src="app.js">
</head>
environment.js
// Declaring environment like that you will have window scoped the variable
// and you will have global access to environment with window.environment
var environment = {apiUrl: 'https://api.url:4100'}
app.js
function example(){
console.log(window.environment.apiUrl); // out https://api.url:4100
}
The approach depends on how you build and/or bundle your AngularJs application. But regardless of that, you'll need to create a config.json file to contain your settings.
If using browserify or webpack, you can import that file via require(...), otherwise you can simply request it via ajax before your app bootstraps.
In any of these cases, the best way to use the configuration data throughout your app is to define it as a constant at the bootstrap phase: app.constant('CONFIG', configData);, assuming that configData is a variable that contains the data from your config.json file.
Then you can use dependency injection to provide CONFIG to all your controllers, services and directives.

How can I improve load performance of Angular2 apps?

Angular2 app is loading slow, how can I improve the performance on load?
I use Angular2, typescript with html5.
currently my app takes 4 seconds to load.
I host with Firebase and use cloudflare.
Things I'm doing / info:
I've compressed images.
I minify css
I minify js.
I use async on my scripts.
My scripts are in my .
The scripts are around 700kb
I used google speed test and get 65%
I used minified version of the libs I use e.g. bootstrap etc.
Using systemjs.
This is the seed app im using: https://github.com/mgechev/angular-seed
Flow:
When the app loads it shows a blue screen (this is the bootstrap css) and then 4 seconds later the app loads and works really fast. But takes 4 seconds to load. It seems the app.js file that systemjs minifies to is slowing the whole app, and not showing the views fast enough.
This is my website speed test:
https://www.webpagetest.org/result/161206_F5_N87/
This is my website:
https://thepoolcover.co.uk/
Let me know if you need more information about my app and any other things I can do.
A single page application generally takes more time while loading as it loads all necessary things at once.
I had also faced same problem and my team has optimized our project from loading in 8 seconds to 2 seconds by using following methods.
Lazy loading a module : Lazy loading modules helps to decrease the startup time. With lazy loading our application does not need to load everything at once, it only needs to load what the user expects to see when the app first loads. Modules that are lazily loaded will only be loaded when the user navigates to their routes. Angular2 has introduced modules in its final release RC5. See below for step-by-step guide.
Aot Compilation : With AoT, the browser downloads a pre-compiled version of the application. The browser loads executable code so it can render the application immediately, without waiting to compile the app first.
It reduces the payload size : There's no need to download the Angular compiler if the app is already compiled. The compiler is roughly half of Angular itself, so omitting it dramatically reduces the application payload. For more info see this.
Webpack : Webpack is a popular module bundler, a tool for bundling application source code in convenient chunks and for loading that code from a server into a browser. You can configure your Angular 2 web application with webpack (see this guide).
Remove scripts,stylesheet from index.html : Remove all scripts and stylesheet which are not needed in index.html. You can load these script dynamically in component itself by calling a service.
Make a file script.service.ts which can load any script on demand for that component
\script.service.ts
import { Injectable } from '#angular/core';
declare var document: any;
#Injectable()
export class Script {
loadScript(path: string) {
//load script
return new Promise((resolve, reject) => {
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = path;
if (script.readyState) { //IE
script.onreadystatechange = () => {
if (script.readyState === "loaded" || script.readyState === "complete") {
script.onreadystatechange = null;
resolve({ loaded: true, status: 'Loaded' });
}
};
} else { //Others
script.onload = () => {
resolve({ loaded: true, status: 'Loaded' });
};
};
script.onerror = (error: any) => resolve({ loaded: false, status: 'Loaded' });
document.getElementsByTagName('head')[0].appendChild(script);
});
}
}
This is just a sample code to load script dynamically, you can customize and optimize it by yourself according to your need.
For stylesheet you should load it in component using styleUrl.
Use Browser Caching : Your webpage files will get stored in the browser cache when you use browser caching. Your pages will load much faster for repeat visitors and so will other pages that share those same resources. For more info https://varvy.com/pagespeed/leverage-browser-caching.html
minimize the code in app.component.ts : minimize the code present in app.component.ts which always run when the app loads or reloads.
set data on app Initialization : if you are using same api calls multiple times in your project or in components,
or you are dependent upon same data in multiple component, instead of calling api multiple times what you can do is save
the data as an object in service on app initialization. That service will act as a singleton throughout the project and you
can access that data without calling api.
Lazy loading of modules step by step
Modular structure : We have to divide our App into separate modules. For example an app may have a user side and an admin side and each will have its own different components and routes, so we will separate this two sides into modules admin.module.ts and user.module.ts.
Root Module : Every Angular app has a root module class. By convention it's a class called AppModule in a file named app.module.ts , this module will import the above two module and also the AppComponent for bootstrap. You can also declare multiple components according to your need. Sample code in app.module.ts:
\app.module.ts
import { NgModule } from '#angular/core';
import { UserModule } from './user/user.module';
import { AdminModule } from './admin/admin.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login.component';
#NgModule({
imports: [UserModule, AdminModule],
declarations: [AppComponent, LoginComponent],
bootstrap: [AppComponent]
})
export class AppModule { }
Routes : Now in your routes you can specify like the following
\app.router.ts
import { ModuleWithProviders } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { LoginComponent } from './login.component';
const routes: Routes = [
{ path: 'login', component: 'LoginComponent' }, //eager loaded
{ path: 'admin', loadChildren: './admin/admin.module#AdminModule' }, // Lazy loaded module
{ path: 'user', loadChildren: './user/user.module#UserModule' } //lazy loaded module
];
Now when the application loads, it will only load LoginComponent and AppComponent code. These modules will only be loaded when we visit /admin or /user routes. Hence it will decrease the size of payload for loading into the browser, thus resulting in fast loading.
Nesting Modules : Just like app.module every module has its own set of components and routes. As your project becomes larger, the nesting of modules inside module is the best way to optimize because we can lazily load those modules whenever we require.
PLEASE NOTE
Above code is only for explanation, please refer for full example
https://angular-2-training-book.rangle.io/handout/modules/lazy-loading-module.html
How about "code splitting".
From the Webpack site:
"For big web apps it’s not efficient to put all code into a single file, especially if some blocks of code are only required under some circumstances. Webpack has a feature to split your codebase into “chunks” which are loaded on demand. Some other bundlers call them “layers”, “rollups”, or “fragments”. This feature is called “code splitting”.
Link here:
https://webpack.github.io/docs/code-splitting.html
Note that the Angular CLI uses Webpack.
Also, make sure that if your app bootstraps with data calls, that you are not holding up the rendering of your components waiting on those calls to return. Promises, async, etc.
It is difficult to diagnose the precise problem you are having without hands-on to your entire code base and backend (as others have suggested, the problem may not be angular at all).
Having said that, I HIGHLY recommend you start using the angular-cli. It was designed by the angular team to accomplish all of the things you need to do in a easy-to-use command line interface. So my answer is predicated on the use of the angular-cli.
Here are the general things you can do to optimize your ng2 project for production:
1) Ahead of Time (AoT) Compilation - Bundling/Minification/Tree-shaking
Look, forget about the headache of configuring a bunch of gulp tasks to accomplish all of these things. The angular-cli handles Bundling/Minification/Tree-shaking/AOT compilation in one easy step:
ng build -prod -aot
This will produce the following js files in your "dist" folder:
inline.d41d8cd98f00b204e980.bundle.js
vendor.d41d8cd98f00b204e980.bundle.js
main.d41d8cd98f00b204e980.bundle.js
styles.4cec2bc5d44c66b4929ab2bb9c4d8efa.bundle.css
You will also get the gzipped versions of these files for MORE optimization:
inline.d41d8cd98f00b204e980.bundle.js.gz
vendor.d41d8cd98f00b204e980.bundle.js.gz
main.d41d8cd98f00b204e980.bundle.js.gz
Angular's AOT compilation will automatically do "tree shaking" on your code and get rid of any unused references. For example, you may use lodash in your project, but you probably only use a few lodash functions; tree shaking will trim away all the unused parts of lodash that are not needed in your final build. And most importantly, AOT compilation will pre-compile all of your code and views which means LESS time the browser needs to get the ng2 app rolling. Click here for more info on angular's AOT compilation.
2) Lazy loading parts of your app
If you further carve up your app into different parts, you do not need to load EVERYING when your app first loads. You can specify different modules for your application that then can be bundled (by the angular-cli aot compiler) into different chunks. Read up here to learn how to organize your project into modules that you can compile into chucks that are only loaded AS NEEDED. Angular-cli will manage the creation of these chunks for you.
3) Angular Universal
Now if you really want to make your load time EXTREMELY fast then you will want to consider implementing Angular Universal, which is when you compile your initial view ON THE SERVER. I have not used Angular Universal as I have been able to achieve fast load times with steps 1 and 2. But it is an exciting option in the ng2 toolset. Keep in mind you don't compile or run the ng2 app on the server, you compile the initial view serverside so the user quickly receives a jolt of html and thus the user PERCEIVES the load time to be very fast (even though a full load will still lag behind a little bit). This step does not obviate the need for the other steps. As a bonus, Angular Universal also is supposed to help with SEO.
4) Secondary bundling
If I am not using lazy loading, I usually go ahead and further bundle the distribution files that are generated from AOT compilation. Thus I create one main.bundle.js file that concats inline.js, vendor.js and main.js files. I use gulp for this.
because its SPA and angular 2 init too slow. thats it. plus RXjs, tons of polifills etc. AOT can help but a lot of angular2 libs do not work with it. angular universal realy helps
Build your angular app using following command to get maximum optimization benefits.
ng build --prod --aot --optimization --build-optimizer --vendor-chunk --common-chunk --extract-licenses --extract-css --source-map=false
Basically you will build in aot mode and use treeshaking with some optimization.
optimization: Enables optimization of the build output.
vendor-chunk: Use a separate bundle containing only vendor libraries.
common-chunk: Use a separate bundle containing code used across multiple bundles.
extract-css: Extract css from global styles onto css files instead of js ones.
build-optimizer: Enables #angular-devkit/build-optimizer optimizations when using the 'aot' option.
source-map=false: remove source map will also reduce bundle size
Try disabling source maps by running ng serve --sourcemap=false

Trying to pass gulp environment-dependent web-pack variables into angularjs app

I'm fairly new to AngularJS and gulp and webpack so excuse me if I'm not using correct terminologies. Been reading stack overflow and angularjs stuff for 2 hours and can't make the connections with what I'm reading.
I'm coming into an already developed application and trying to find the best way to include analytics API keys from a webpack plugin variables into the AngularJS app to use.
The directory is setup as such:
ng_organize
/gulp
/tasks
webpack-development.js
webpack-production.js
/util
/src
/appName
/customer
CustomerController.js
...
/home
/shop
app.js
index.js
application.js
The webpack variables in ng_organize/gulp/tasks/webpack-development.js are:
gulp.task('webpack:development', function(callback){
webpack({
context: ...
plugins: [
new webpack.DefinePlugin {
GOOGLE_ANALYTICS_KEY: 'XXX',
...
}
]
});
});
Currently, the webpack variables can be accessed in ng_organize/application.js with GOOGLE_ANALYTICS_KEY. I'm trying to access them within ng_organize/src/appName/customer/CustomerController.js.
I want to create a service to store GOOGLE_ANALYTICS_KEY (and other keys) that is dependent on the environment. Is this possible? If so, how would I go about doing it?
It turns out they are automatically included globally in your app's code, you just won't be able to call the global variables in the debugger (which was how I was testing to see if they were accessible inside the CustomerController).
GOOGLE_ANALYTICS_KEY is still accessible inside CustomerController.

AngularJS inheritance of 'config' from one module to another

I have two modules, like the following ones:
var a = angular.module('a', []);
a.config(['$interpolateProvider', function($interpolateProvider) {
$interpolateProvider.startSymbol('<[');
$interpolateProvider.endSymbol(']>');
}]);
var b = angular.module('b', ['a']);
I'm running some tests, but I can't figure out if the interpolate configuration in a module is being inherited in b module.
Does angular inherit the config of modules into another modules?
Inheritance isn't the issue here, you are configuring a provider that is used by both modules, and angular is going to apply each config in the order that you register them. From the docs:
When bootstrapping, first Angular applies all constant definitions.
Then Angular applies configuration blocks in the same order they were
registered.
You can reset the values of $interpolateProvider start and end symbols, but you cannot have both settings in your application since you are modifying the same provider in each config block.
Here's a plunk showing this in action.

Externalize service configuration in several angular apps that has the same configs

I have several AngularJS apps that they all have some common dependencies like angular-translate. All the angular apps have the same configuration (in app.js) for angular-translate.
I am looking for a way to externalize the configuration of angular-translate in all these apps. In a way that I will make the changes in one place (maybe a service?) and then the configs will be applied to the apps.
Btw, I am new to Angular world, your precise suggestions would be appreciated.
You can create an angular module config function in a separate project that all of your projects in your projects import and use. Angular allows you to have as many module().config functions as you want. The same goes for .run functions.
Example
// Some common file available to all projects
angular.module('common-config', [
'angular-translate'
/* other common dependencies here */
]).config(['$translateProvider',
function ($translateProvider) {
$translateProvider.preferredLanguage('en');
// Other configuration here.
}]);
// App 1
angular.module('app-one', ['common-config']).run(/*...*/);
// App 2
angular.module('app-two', ['common-config']).run(/*...*/);
// App 3
angular.module('app-three', ['common-config']).run(/*...*/);

Categories

Resources