How to use environment variable in index.html for Angular 6 - angular6

I am using angular6, in my project I am using Facebook Account Toolkit for mobile verification purpose.
I need to initialise Account toolkit in index.html file using following code.
AccountKit.init({
appId:"XX",
state:"xx",
version:"v1.2",
fbAppEventsEnabled:true,
debug:true
});
The problem is, values for appId and state change depending on environment (development/test/production).
How can I use environment variables in index.html file.
Please let me know if anyone has a solution for angular 6.

You should create copy of index.html and name it index.someenv.html.
Then in your angular.json in environment configuration setup file replacement:
"fileReplacements": [
{
"replace": "src/index.html",
"with": "src/index.someenv.html"
}
]
The angular cli will replace these files when you run your build

This answer supersedes Artyom's answer for Angular 8 and above. Add the following to your angular.json:
"production": {
"index": {
"input": "src/index.someenv.html",
"output": "index.html"
},
},

An example here for document.write(environment.variable) :
https://github.com/angular/angular-cli/issues/4451#issuecomment-285026543
import { environment } from './environments/environment';
if (environment.production) {
document.write('<script type="text/javascript">// ProductionAnalyticsCodeHere</script>');
} else if (environment.staging) {
document.write('<script type="text/javascript">// StagingAnalyticsCodeHere</script>');
}

In main.ts file you can use document.write(environment.variable) and it will write what you want in index.html
(I use it to make the Google Analytics script take a dynamic Tracking ID wether it's in development or production mode, and it works well in Angular6)

for me above answers did not work on Angular 10, so I created different folder for staging, production etc and placed the index.html which was used by CLI as per the build environment
{
"projects": {
"yourApp": {
"projectType": "application",
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "#angular-devkit/build-angular:browser",
"options": {
"index": "src/index.html",
},
"configurations": {
"production": {
"index": "src/production/index.html",
}
}
}
}
}
}
}

Avoid direct access to the document object by injecting DOCUMENT to your AppComponent.
import { DOCUMENT } from '#angular/common';
...
public constructor(
#Inject(DOCUMENT) private doc: any
) {
Add the tag in ngOnInit()
ngOnInit() {
this.setYourScriptTag();
}
private function to set it
private setYourScriptTag() {
const s = this.doc.createElement('script');
s.type = 'text/javascript';
s.innerHTML = `AccountKit.init({
appId: "${environment.appId}",
state: "${environment.state}",
version:"v1.2",
fbAppEventsEnabled:true,
debug:true
});`;
const head = this.doc.getElementsByTagName('head')[0];
head.appendChild(s);
}
This answer is from edodusi
https://github.com/angular/angular-cli/issues/4451#issuecomment-384992203

I think you can do it all in main.ts
const env = environment;
AccountKit.init({
appId:env.appId, // this lane
state:env.state, // this lane
version:"v1.2",
fbAppEventsEnabled:true,
debug:true
});
Thanks.

I added this in main.ts:
var html = document.documentElement.innerHTML
document.documentElement.innerHTML = html.replace("Replace me!", environment.variable)
Note that the old value will still exist in index.html for some time while the page is initially loading. (For example, use this to replace the page title and you'll see the old value displayed before the replace happens.)

import your environment file into .ts file.
import { environment } from '../../environments/environment';
Create required fields in your class, assign values from environment to these variables in the constructor, use usual binding in the .html file.
.ts
import { Component } from '#angular/core';
import { environment } from '../environments/environment';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
public production = true;
constructor() {
this.production = environment.production;
}
}
.html
<span>{{production}}</span>

Related

Ionic ReferenceError: google is not defined

I am trying to find the distance between a location input from the user and a fixed location. I want to use the DistanceMatrix service from google platform, but I keep getting the "google is not defined" error.
import { Component } from '#angular/core';
import { Router } from '#angular/router';
import { AlertController, ToastController } from '#ionic/angular';
import { DataService } from '../data.service';
declare var google : any;
#Component({
selector: 'app-checkout',
templateUrl: './checkout.page.html',
styleUrls: ['./checkout.page.scss'],
})
export class CheckoutPage {
payment = ""
name : string = ""
phone : string = ""
address : string = ""
constructor(private alertController : AlertController, private router:Router, private dataService : DataService, private toastController : ToastController) {
var to = new google.maps.places.Autocomplete(document.getElementById("address") as HTMLInputElement)
}
I would also like for the input to autocomplete as the user types.
I have been stuck on this for 3 days now.
Any help will be greatly appreciated.
I will answer this with a little delay, but one way to fix it is as follows:
First you need to install #types/googlemaps :
npm install --save #types/googlemaps
Then if you are using Angular-cli, add googlemaps to types in src/tsconfig.app.json:
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": [
"googlemaps"
]
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
After that, you probably won't have this problem anymore.
source: https://roufid.com/angular-uncaught-referenceerror-google-not-defined/

How to read a local JSON into Angular typescript file

I'm using primeng Datepicker. It's [locale] property accepts languages that I'm storing in a local json called datePickerLanguages.json:
{
"DatePickerLanguages": [{
"spanish": {
"firstDayOfWeek": 1,
"dayNames": ["domingo", "lunes", ...],
"dayNamesShort": ["dom", "lun", "mar", "mié", "jue", "vie", "sáb"],
"dayNamesMin": ["D", "L", "M", "X", "J", "V", "S"],
"monthNames": [
"Enero",
"Febrero",
...
"Diciembre"
],
"today": "Hoy",
"clear": "Borrar"
}
},
{
"german": {
"firstDayOfWeek": 1,
"dayNames": ["Sonntag", "Montag",...]
...
"today": "Heute",
"clear": "Klar"
}
}
]
}
I've to pass spanish, german or any other language (one language at a time) into the [locale]=xxxxx of p-calendar. My typescript is:
import { Component, OnInit, VERSION } from "#angular/core";
import languages from "./datePickerLanguages.json";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
name = "Angular " + VERSION.major;
value;
languageList: {}[] = languages;
selectedLocale;
ngOnInit() {
this.selectedLocale = this.languageList.spanish;
}
}
template file:
<p-calendar [(ngModel)]="value" name="test" [locale]="selectedLocale">
</p-calendar>
I'm getting:
Property 'spanish' does not exist on type '{}[]'.
This is what I followed:
How to Read Local JSON file in Angular
Here is the stackblitz.
Please point out my mistakes.
Try this:
languageList: {
'DatePickerLanguages'
} = languages;
ngOnInit() {
this.selectedLocale = this.languageList.DatePickerLanguages[0].spanish;
}
If you have control over the JSON file. I would suggest you to modify your JSON as such:
{
"DatePickerLanguages": { // Initialized it as object instead of array.
"spanish": {
....
},
"german": {
....
}
}
}
And access the spanish by using:
this.selectedLocale = this.languageList.DatePickerLanguages.spanish;
As per the request in your comments:
The reason to use the zeroth index in my answer is because in your JSON the "DatePickerLanguages": is an array []. The property spanish lies in the zeroth index of your array.

Replace an Url from Json with a button that gives URL to another function

I’m trying to implement a function in my web application, that can discover URLs in a loaded Json via Regular Expressions using angular. Afterwards the URLs get replaced with buttons and when the button gets clicked the exact URL who got replaced gets handed in another function in another component which loads the given URL.
Until now I’m at the point that I can replace the URL of the loaded JSON with a button. I’m using a pipe for that named transform-url.pipe:
import {
Pipe,
PipeTransform,
Input,
Component
} from '#angular/core';
import {
DomSanitizer
} from "#angular/platform-browser";
#Pipe({
name: 'transformUrl',
pure: false
})
export class TransformUrlPipe implements PipeTransform {
constructor(protected sanitizer: DomSanitizer) {}
transform(value: any, ): any {
if (value.match === 0) {
return value;
}
return this.sanitizer.bypassSecurityTrustHtml(
value.replace(/"url:\/\/.*\/.*\/.*"/g, "<button type='button' >Run</button>")
);
}
}
Hmtl:
<h3>Unrecognized JSON data:</h3>
<pre [innerHTML] = "genericJson | transformUrl"></pre>
Sample of Json:
"documentVersion": "1.0",
"documentType": "Urls",
"name": {
"request": {
"version": "1.0",
"abc": [
{
"productUrl": "url://awrtwtgsfgshsfh/sfg/v1/document/jsfhljhl564356lhgljhsljh5895hj",
"attributes": [
{
"attributeSet": {
"attributes": {
"426824828246824828282468248": {
"value": "1"
},
"647474373748648248282824": {
"value": "true"
}
}
}
}
]
},
"productUrl": "url://adgagjfjfjfjhf/sfg/v1/document/adfah5ahfah5jahlkhaliohßjkjlaß73-3",
"attributes": [
{
"attributeSet": {
"attributes": {
"426824828246824828282468248": {
"value": "1"
},
"647474373748648248282824": {
"value": "true"
}
}
}
}
]
},
I found nothing on google on how to do this specific task. Is a pipe even the best solution for this? I tried to implement functions in the pipe but it didnt work.
Another thing that i cant figure out is how i can make every button unique so the application knows which excat URL it should take when the button is clicked? And how can I select the URL and give it in another function in another component?
First of all rather than using pipe, I have created solution in Component only.
Stringify JSON which needs to be get values from. genericJson
Remove first and last ", which is matched in regex.
Using *ngFor, create buttons and pass separate link to click function.
Demo (check console for button click)
EDIT: NEW DEMO.
import { Component } from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
name = "Angular";
genericJson = { ... some json ... }
formatedOutput: (string | boolean)[] = [];
ngOnInit() {
// adding formatting to make regex search easier
const jsonFormattedString = JSON.stringify(this.genericJson, null, 4);
this.formatedOutput = jsonFormattedString.split('\n');
}
onClick(out: string) {
// do whatever operation on link
const link = out.match(/url:\/\/.*\/.*\/.*/)[0];
console.log(link);
}
urlFound(out: string): boolean {
const match = out.match(/"url:\/\/.*\/.*\/.*"/);
if (match !== undefined && match !== null && match.length > 0) {
return true;
}
return false;
}
}
Use matched links in HTML template,
<div>
<div *ngFor="let out of formatedOutput">
<ng-container *ngIf="urlFound(out); else simple_json"><pre>{{out}}<button (click)="onClick(out)">Link</button></pre></ng-container>
<ng-template #simple_json><pre>{{out}}</pre></ng-template>
</div>
</div>
I think, you should do it like this instead of using pipe:
.html
<button (click)="goTo(genericJson.url)">Run</button>
.ts
genericJson = {
url: "www.google.com"
};
goTo(url: string) {
if (url) {
if (!url.includes("https")) {
url = "https://" + url;
}
window.open(url, "_blank");
}
}

Polymer 3 "Uncaught ReferenceError: Polymer is not defined"

I am new to polymer and I have the following problem after running "polymer build" and when I run "polymer serve build / esm-bundled" the following error occurs in the browser:
Uncaught ReferenceError: Polymer is not defined
I noticed that if I disable the polymer.json's bundle the error does not appear.
Can someone help me please?
I'm using Polymer-3 and Redux, this project was built initially with the polymer-2, but I did the conversion and it works fine when I run before the build.
This is polymer.json
{
"entrypoint": "index.html",
"shell": "src/components/conversion-today-app/conversion-today-app.js",
"sources": [
"src/**/*",
"images/**/*",
"robots.txt",
"sitemap.xml",
"coinzilla-354635a9db1dbd05d0.txt"
],
"extraDependencies": [
"manifest.json",
"node_modules/web-animations-js/**",
"node_modules/#webcomponents/webcomponentsjs/**",
"node_modules/#polymer/polymer/**"
],
"builds": [
{
"name": "esm-bundled",
"browserCapabilities": [
"es2015",
"modules"
],
"js": {
"minify": true
},
"css": {
"minify": true
},
"html": {
"minify": true
},
"bundle": true,
"addServiceWorker": true
}
]
}
Part of the component where the error is
// Principais // Bibliotecas
import { PolymerElement,html } from '#polymer/polymer/polymer-element.js';
// import { html } from '#polymer/polymer/lib/utils/html-tag.js';
import { afterNextRender } from '#polymer/polymer/lib/utils/render-status.js';
import { connect } from 'pwa-helpers/connect-mixin.js';
// pwa helper
import { installOfflineWatcher } from 'pwa-helpers/network.js';
// ---------- Redux ----------
// This element is connected to the redux store.
import { store } from '../../store.js';
// These are the actions needed by this element.
import { addCoin } from '../../actions/converter.js';
import converter from '../../reducers/converter.js';
import { fetchCrypto } from '../../actions/cryptoCoins.js';
import { fetchCurrencyState } from '../../actions/stateCoins.js';
// We are lazy loading its reducer.
import cryptoCoins from '../../reducers/cryptoCoins.js';
import stateCoins from '../../reducers/stateCoins.js';
store.addReducers({
stateCoins, cryptoCoins
});
// Componentes de Terceiros
import '#polymer/paper-fab/paper-fab.js';
import '#polymer/paper-dialog/paper-dialog.js';
import '#polymer/paper-dropdown-menu/paper-dropdown-menu-light.js';
import '#polymer/paper-dropdown-menu/paper-dropdown-menu.js';
import '#polymer/paper-button/paper-button.js';
import '#polymer/iron-demo-helpers/demo-snippet.js';
import '#polymer/iron-demo-helpers/demo-pages-shared-styles.js';
import '#polymer/paper-item/paper-item.js';
import '#polymer/paper-listbox/paper-listbox.js';
import '#polymer/iron-ajax/iron-ajax.js';
import '#polymer/app-storage/app-localstorage/app-localstorage-document.js';
// Components locais
import '../elements/collection-coin-element.js';
class ConversionTodayConverter extends connect(store)(PolymerElement) {
static get template() {
return html`
<style include="demo-pages-shared-styles">
:host {
display: block;
padding: 10px;
}
paper-fab {
--paper-fab-background: #2B4461;
display: inline-block;
/* margin: 8px; */
position: fixed;
right: 25px;
bottom: 30px;
}
</style>
<app-localstorage-document key="conversion-today-coins" data="{{coins}}">
</app-localstorage-document>
Browser -- error
This is what appears in the browser
First of all: You should add some code examples of your elements, since it it's not easy to solve your problem without any further informations 😉
When I upgraded my projects to polymer 3.x I always got that console error when I forgot to replace usages of the legacy Polymer class. For example: In Polymer-2 an element was defined by class XCustom extends Polymer.Element {...} but in polymer-3 it is class XCustom extends PolymerElement {...}. If you forget to change that in any element (your own and the imported ones), your console with throw the Polymer is not defined-error.
So I guess in your Webapp/Element there are still references to the legacy Polymer-class. In my case it was mostly on of these usages: Polymer.Element, Polymer.importHref(...) or Polymer.mixinBehaviors(...). You should simple search your project files for the usage of "Polymer." and replace all occurrences with the equivalent polymer-3-functions.
Here are some of the most common upgrade replacements in polymer-3:
Polymer.Element
Create an element as follows:
import { PolymerElement, html } from '#polymer/polymer/polymer-element.js';
class MyElement extends PolymerElement {...}
Also have a look at the Polymer docs
Polymer.mixinBehaviors(...)
Use behavior mixins as follows:
import {IronResizableBehavior} from '#polymer/iron-resizable-behavior/iron-resizable-behavior.js';
import { mixinBehaviors } from '#polymer/polymer/lib/legacy/class.js';
class MyElement extends mixinBehaviors([IronResizableBehavior],PolymerElement) {...}
Polymer.importHref(...)
Import elements as follows:
import('./my-page.js').then(
function(){
console.info("Success");
}.bind(this),
function(){
console.info("Fail");
}.bind(this)
);
Also have a look at the Polymer docs

Load Config JSON File In Angular 2

I want to load Constant File in Angular 2(which is a Normal TypeScript File) having WebAPI EndPoints.
In Angular1.x. we used to have constants for the same.
How in Angular 2 I can Implement the Same?
I have created the .ts file.My main concern lies in how to load the file beforehand every Other class File loads.
.ts file :
export class testAPI {
getAPI = "myUrl";
}
In service file I am using the same by doing Normal Import:
constructor(private http: Http) {
//console.log(this.test);
console.log(this.testing.getAPI);
//this.test.load();
}
I am getting the Console as Undefined.(Must be because my Service class is loading before API Class).
Thanks in Advance.
UPDATES
Inspired with the solution for this particular problem created ngx-envconfig package and published it on NPM registery. It has the same functionalities as it is provided in this answer and even more.
You can have the JSON file somewhere in assets folder like: assets/config. Depending on whether the environment is dev or not you can use two .json files, one for development and one for production. So you can have development.json and production.json files, where each one will keep the appropriate API endpoints.
Basically you need to go through the following steps:
1. Setting up environment (skip this step if you have it already)
Create two files in src/environments folder:
environment.prod.ts
export const environment = {
production: true
};
environment.ts
export const environment = {
production: false
};
2. Create JSON config files
assets/config/production.json
{
"debugging": false,
"API_ENDPOINTS": {
"USER": "api/v1/user",
...
}
}
assets/config/development.json
{
"debugging": true,
"API_ENDPOINTS": {
"USER": "api/v1/user",
...
}
}
3. Create a service as follows
Note depending on the environment, the ConfigService will load the appropriate file
import { Injectable, APP_INITIALIZER } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs';
import { environment } from 'environments/environment'; //path to your environment files
#Injectable()
export class ConfigService {
private _config: Object
private _env: string;
constructor(private _http: Http) { }
load() {
return new Promise((resolve, reject) => {
this._env = 'development';
if (environment.production)
this._env = 'production';
console.log(this._env)
this._http.get('./assets/config/' + this._env + '.json')
.map(res => res.json())
.subscribe((data) => {
this._config = data;
resolve(true);
},
(error: any) => {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
});
});
}
// Is app in the development mode?
isDevmode() {
return this._env === 'development';
}
// Gets API route based on the provided key
getApi(key: string): string {
return this._config["API_ENDPOINTS"][key];
}
// Gets a value of specified property in the configuration file
get(key: any) {
return this._config[key];
}
}
export function ConfigFactory(config: ConfigService) {
return () => config.load();
}
export function init() {
return {
provide: APP_INITIALIZER,
useFactory: ConfigFactory,
deps: [ConfigService],
multi: true
}
}
const ConfigModule = {
init: init
}
export { ConfigModule };
4. Integrate with app.module.ts
import { NgModule } from '#angular/core';
import { ConfigModule, ConfigService } from './config/config.service';
#NgModule({
imports: [
...
],
providers: [
...
ConfigService,
ConfigModule.init(),
...
]
})
export class AppModule { }
Now you can use ConfigService wherever you want get the necessary API endpoints defined in config .json files.
In Angular 4+ projects generated with the Angular CLI, you will have the environment folder out-of-the-box. Inside of it, you will find the environment.ts files from Karlen's answer. That is a working solution for configuration with one caveat: Your environment variables are captured at build time.
Why does that matter?
When you're setting up a CI/CD pipeline for your Angular app, you will generally have a build tool that builds your project (like Jenkins) and a deployment tool (like Octopus) that will grab that package (the dist folder) and deploy to the selected environment, replacing your environment variables with the correct values in the process. If you use the environment.ts files, your environment variables cannot be replaced this way because the environment.ts files do not get included in the dist folder. There is no file your deployment tool can pick up and edit.
What can we do? we can add a JSON configuration file inside of the assets folder. Those files are included by default in the dist folder we will want to deploy. When we want to use an environment variable, we simply import the settings like import config from '[relative/path/to/your/config/file.json]'.
When we do this, we will get something like the following error:
Cannot find module '../../config.json'. Consider using '--resolveJsonModule' to import module with '.json' extension
This is because the typescript compiler tries to import an exported module and cannot find one. We can fix this by adding the following JSON properties/values in our tsconfig.json file.
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
resolveJsonModule allows the typescript compiler to import, extract types from, and generate .json files.
allowSyntheticDefaultImports allows default imports from modules with no default export.
With this in place, we can run our project and we will find that our error is gone and we can use our config values without any issues.
Now, because this config file is included in the dist folder that gets deployed on the server, we can configure our deployment tool to replace the variable values with the values specific to the environment to which we want to deploy. With this in place we can build our Angular app once and deploy it anywhere.
Another added benefit is that most deployment tools like Octopus ship with native JSON support so you can configure it to replace environment variables in your JSON file quite easily. The alternative is using a regex solution to replace environment variables in a .ts file, which is comparatively more complicated and prone to mistakes.
It is possible to import JSON in TypeScript. You need to add typings:
typings.d.ts:
declare module "*.json" {
const value: any;
export default value;
}
And then import like this:
import config from "../config/config.json";
config.json:
{
"api_url": "http://localhost/dev"
}
I had same issue and in the end i give up from .ts and put it in .js :D like this:
configuration.js in root
var configuration = {
'apiHost': 'http://localhost:8900',
'enableInMemoryWebApi': false,
'authMode': 'standalone',
'wsUrl': 'ws://localhost:8900/ws'
};
module.exports = configuration;
in .ts file for ex. user.service.ts
let configuration = require('../configuration'); //in import section
#Injectable()
export class UserService {
...
getUser(id: number | string): Promise<User> {
console.log(configuration.apiHost) //will get propertye from .js file
return this.http.get(`${configuration.apiHost}/${id}`, this.headers).toPromise().then(this.extractData).catch(this.handleError);
}
}
Hope it helps
You can use Opague token to set constant values as providers
Try:
In your const file:
import { OpaqueToken } from '#angular/core';
export const CONFIG_TOKEN = new OpaqueToken('config');
export const CONFIG = {
apiUrl: 'myUrl'
};
In your AppModule set to make it a singleton provider for the app:
providers:[
//other providers,
{provide: CONFIG_TOKEN, useValue: CONFIG}
]
For injecting in constructor,
constructor( #Inject(CONFIG_TOKEN) private config)