Load Script Tag in Angular 2 App When Src Attribute is from Web API Call - html

Context:
I have an Angular 2+ application that makes calls to a web API containing URLs for a src attribute on a script tag that is created by a loadScript function in the AfterViewInit lifecycle hook.
The web API returns a JsonResult and is yielding the data I expect. I was able to interpolate some of the data in the component's template.
Additionally, before I added the call to the web API, the loadScript function was working with a hard-coded argument.
Reading a thread on github. A "member" stated that scripts are not supposed to be loaded on demand. So what I implemented with the loadScript function is essentially a work around, but how else would load them? I don't want to have a seemingly endless amount of script tags sitting in the index.html file.
import { Component, OnInit, AfterViewInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { Http } from '#angular/http';
#Component({
selector: 'app-agriculture-roadmap',
templateUrl: './agriculture-roadmap.component.html',
styleUrls: ['./agriculture-roadmap.component.css']
})
export class RoadmapComponent implements OnInit, AfterViewInit {
constructor(private _httpService: Http, private _route: ActivatedRoute)
{
}
apiRoadmaps: { roadmapName: string, pdfRoadmapURL: string, jsRoadmapURL: string };
ngOnInit() {
this._httpService
.get('/api/roadmaps/' + this._route.params)
.subscribe(values => {
this.apiRoadmaps = values.json() as { roadmapName: string, pdfRoadmapURL: string, jsRoadmapURL: string };
});
}
async ngAfterViewInit() {
await this.loadScript(this.apiRoadmaps.jsRoadmapURL);
}
private loadScript(scriptUrl: string) {
return new Promise((resolve, reject) => {
const scriptElement = document.createElement('script')
scriptElement.src = scriptUrl
scriptElement.onload = resolve
document.body.appendChild(scriptElement)
})
}
}

If you are using angular cli .
Then place these scripts in
angular-cli.json file under scripts array
scripts:[
.....
]
Please refer this [link] (https://rahulrsingh09.github.io/AngularConcepts/faq)
It has a question on how to refer third party js or scripts in Angular with or without typings.

Related

Problem with reading from a json file when refreshing th page Angular

I have an angular application and the client wants the path of the Backend in a json file, so he can change it easily whithout needing of another deployment.
Well i did it, but when i refresh the page or close the app and reopen it, the app don't detect the path of the backend, it is like a problem of retard or synchronisation.
This is the error in the console :
http://***/undefinedapi/Leave/GetlistLeave
This is how i did it :
The json file :
{
"ApiRoot": "http://***/"
}
How i read from the constant from the json file :
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs';
import { apiRoot } from '../model/model.apiRoot';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class apiRootService {
static apiRoot: string;
constructor(private http: Http) { }
public initialiseApiRoot()
{
this.http.get('./assets/apiRoot/apiRoot.json').pipe(map((response: Response) =>
<apiRoot>response.json())).subscribe(data => {
apiRootService.apiRoot = data['ApiRoot'];
})
}
}
and then i call this function in the constructor of app.component.ts like this :
this.apiRootService.initialiseApiRoot();
and change the call of the api in every servic elike this :
return this.http.get(apiRootService.apiRoot + .....
Any hlp and thanks
Well, let's suppose you're not facing a cache problem. If it isn't a cache problem, maybe it's a matter of timing.
You can try to set your apiRoot while your app is initializing (before app.component.ts is loaded). You can do that by providing an APP_INITIALIZER as described in Angular docs. If you use a factory that returns a function providing a promise, you'll delay your app initialization until your json file is loaded so you can initialize apiRoot. A factory is a useful approach because it will allow you to inject HttpClient service during initialization in the provider (you'll need it to get your json file).
You can do something like (in your app.module.ts):
...
import {APP_INITIALIZER} from '#angular/core';
...
// Angular will inject the HttpClient because you'll
// tell it that this is a dependency of this factory
// in the providers array
export function getApiRoot(http: HttpClient) {
return () => {
return this.http.get('./assets/apiRoot/apiRoot.json').pipe(
map((response: Response) => <apiRoot>response.json()),
tap((data: any) => apiRootService.apiRoot = data['ApiRoot'])
).toPromise();
};
}
...
#NgModule({
imports: [
...
HttpClientModule,
...
],
providers: [
...
{
provide: APP_INTIALIZER,
useFactory: getApiRoot,
multi: true,
deps: [HttpClient]
}
...
]
})
export class AppModule {}
because you are going with wrong approach. you are seeting url after application is initialized. Refer :- https://medium.com/voobans-tech-stories/multiple-environments-with-angular-and-docker-2512e342ab5a. this will give general idea how to achieve build once and deploy anywhere

Importing JSON from a server within Angular

I am reading data from a JSON, which is one a server and it updates regularly and changes. I need to be able to read this JSON from the server so that I display the most up to date information on my web page.
Currently, the to be able to read the JSONs they are stored within the same project folder as my angular project. (This was because they were not set up on the server when I started).
This is how I currently import the JSON to be able to read it:
import jsonInfo from '../../../info.json'
I thought I would be able to change the file link to the server address, like so:
import jsonInfo from 'http://servername/folder/info.json'
But, VSCode gives me an error: Cannot find module 'http://servername/folder/info.json'
This is definitely the location of the JSON I am trying to load because when I click the link it takes me to the JSON and displays it.
My question is, how do I import the JSON into my .ts from a server so that I can keep getting the updated information from the JSON?
JSON file on a server is just like any other web resource you would try to access (like an API endpoint, for example).
So you should use built in angular http client to access this JSON file.
For example:
import { HttpClient } from '#angular/common/http';
export class SomeService {
constructor(private http: HttpClient) { }
getInfo() {
return this.http.get('http://servername/folder/info.json');
}
}
//...
export class SomeComponent implements OnInit {
info: any;
constructor(private someService: SomeService) {}
ngOnInit() {
this.someService.getInfo().subscribe(info => this.info = info)
}
}
Use HttpClient get method.
this.httpClient.get('http://servername/folder/info.json').subscribe(data => {
// your logic
})
You can use HttpClient and do like as shown below
Working Demo
import { Component, OnInit } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular';
data = [];
apiUrl = 'http://servername/folder/info.json';
GetData() {
this.http.get<any[]>(this.apiUrl)
.subscribe(data => {
this.data = data;
});
}
ClearData() {
this.data = [];
}
constructor(private http: HttpClient) {}
ngOnInit() {}
}

JSON service returning undefined property with Angular 7

This should be the simplest thing. I have a component that calls a service that imports a local JSON directly (see Import JSON directly with Angular 7)
It reads the JSON contents fine, but the pages property is undefined. I think I set everything up based on the StackBlitz in that link, there doesn't seem to be much to it. There isn't any HTML yet, this is all just via the console. It's in app.component.html.
Reading local json files json.service.ts:14
[{…}]0: {name: "Home"}length: 1__proto__: Array(0) json.service.ts:15
undefined home.component.ts:31
json.service.ts:
import { Injectable } from '#angular/core';
import SampleJson from '../assets/SampleJson.json';
export interface JsonInterface {
name: any;
}
#Injectable()
export class JsonService {
ngOnInit() {
console.log('Reading local json files');
console.log(SampleJson);
}
}
home.component.ts:
import { JsonService, JsonInterface } from '../json.service';
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
constructor(service: JsonService) {
service.ngOnInit();
};
#Input() pages: JsonInterface;
ngOnInit() {
console.log(this.pages);
}
}
Sample.json
{ "name":"Home" }
If I understand your log correctly, it works as expected:
constructor(service: JsonService) {
service.ngOnInit();
};
You request the service and you get an instance. Then you call ngOnInit:
ngOnInit() {
console.log('Reading local json files');
console.log(SampleJson);
}
Now it logs the "reading…" and the content of your json file.
ngOnInit() {
console.log(this.pages);
}
Then you log this.pages which is empty. You never filled it. You never did anything with your service or the data loaded in your service.
I think what you want is something like this
export class JsonService {
getPages() { return SampleJson; }
}
and in your component:
constructor(private service: JsonService) {}
ngOnInit() {
this.pages = this.service.getPages();
console.log(this.pages);
}
The sample code is not tested but I think you've got the point.
The problem is with pages. You have inly declared 'pages' as 'JsonInterface' which is only the type of 'pages' but never initialized with any value so it is undefined.. you need to write a function in Service as the above answer by #Christoph .
I hope you understand why this error occured and If you are not inputting a value to 'pages' from html you don't need to declare it as '#Input'.

share service data b/w two component in angular 2 using get set method

Here is my first component
requirement:display data in second component which is sent by first component.
current status: i set data into service get set method and get also the data data but unable to display same data which is set by first component template .
import { Component } from '#angular/core';
import { ConfigService } from './myservicedata';
import { Router } from '#angular/router';// add file for navigate from one page to another page
#Component({
selector: 'tab-one',
templateUrl: './tabone.component.html',
providers:[ConfigService]
})
export class taboneComponent {
constructor(public configservice:ConfigService,private router:Router) {}
formData(data:any){
this.configservice.set_service_data(data);
console.log("value of data which is set by me into service"+ data);
}
// for navigate from one url to another url
navigate(){
this.router.navigateByUrl('/tab_two');
}
}
Here is my second component
import { Component ,OnInit} from '#angular/core';
import { ConfigService } from './myservicedata';
import 'rxjs/Rx'; // add this file for use the map feature.
#Component({
selector: 'tab-two',
templateUrl: './tabtwo.component.html',
// providers:[ConfigService]
})
export class tabtwoComponent {
public getterSetter:any=[];
// public store_service_data:any=[];
constructor(private configservice:ConfigService) {}
ngOnInit(){
this.configservice.get_service_data()
}
showdata(){
console.log( this.configservice.get_service_data());
}
};
Here is my service
import {Injectable} from '#angular/core';
import {Http,Response} from "#angular/http";
import {Observable} from 'rxjs/Rx';
import 'rxjs/add/operator/map';
#Injectable()
export class ConfigService {
private _url:string="../mockData.json";
public serviceData:any=[];
get_service_data():any{
return this.serviceData;
// this.serviceData.map(
// (response:Response) => response.json()
// );
};
set_service_data(value:any):void{
this.serviceData=value;
};
constructor(private http:Http) {}
// Uses http.get() to load a single JSON file
getFriendsData():any {
return this.http.get(this._url).map(
(response:Response) => response.json()
);
}
};
Remove the providers array in your TaboneComponent and make sure ConfigService is in the providers array in your app.module.ts
Explanation:
In order to retrieve the information from the service what you want is to make sure that both your components reference the same instance on your ConfigService
Angular uses hierarchical dependency injection, which means whenever a dependency like your ConfigService is requested, Angular will traverse up the component tree to find a place where it has already been provided and pass that instance to the requester.
Because of this, you can easily create a singleton instance by providing a service in your app module as every component is a child of this.
When you provide the service like you have done in your TaboneComponent you are saying give me a new instance of this service even if one has already been provided somewhere else. Any component that is a child of tab-one will be able to get the data you have set in that service, but anything that is not a child will not.
You can read more about Angular's dependency injection here:
https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html

http with Observable in Angular 2 cant use data

i am new to angular 2 and to observables but i wanted to give it a shot. So i have installed the angular-cli and made a simple test project.
All i wanted it to do is read a json file and work with the data inside of a component (the first intention was to make a service but i wanted to start on a low basis).
So i have created a json file in the assets/json folder (testjson.json):
{
"teststring": "test works"
}
then i have imported the http from angular and the rxjs map stuff inside of my content.component.ts file:
import { Component, OnInit } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Component({
selector: 'app-content',
templateUrl: './content.component.html',
styleUrls: ['./content.component.css']
})
export class ContentComponent implements OnInit {
title: string = "Default";
data;
constructor(private http:Http) {
http.get('assets/json/testjson.json').map(res => res.json()).subscribe(data => {this.data = data; this.title = data.teststring; console.log(this.data);});
}
ngOnInit() {
}
}
So far so good, the app prints out the following:
app works!
test works [object Object]
But i want to use this data in the whole component, not only in the constructor. but if i try to console.log "this.data" outside of the constructor (inside the ngOnInit function), it prints undefined in the console.
I know, that it must have something to do with asynch loading but unfortunately i have no clue how to tell the app to wait until this.data is filled.
I hope you can help me with that. Of course in the future i want a service which does that kind of stuff and more than one component should grab data from it.
Thanks in advance!
You should move the initialization code to the initialization method.
Your data becomes available once the callback completes. In your template you can use *ngIf to execute code inside a block once there is data. As long as the *ngIf does not eval to true the inner code will not run.
The only way you can run console.log(data) is from inside the callback or called from the callback because you have to wait until the data is loaded.
content.component.html
<div *ngIf="data">
<span>{{data.teststring}}</span>
</div>
content.component.ts
export class ContentComponent implements OnInit {
title: string = "Default";
data: any = null;
constructor(private http:Http) {
}
ngOnInit() {
this.http.get('assets/json/testjson.json')
.map(res => res.json())
.subscribe(data => {
this.data = data;
this.title = data.teststring;
console.log(this.data);
});
}
}
Edit
In response to the comment below If you abstract out the http call to a service you can see the exact same logic still applies. You are still using the concept of a promise of data and that you can subscribe to that promise once it has completed. The only difference here is the http call is abstracted to a different class.
content.component.ts
export class ContentComponent implements OnInit {
title: string = "Default";
data: any = null;
// inject service
constructor(private contentService:ContentService) {
}
ngOnInit() {
this.contentService.getData()
.subscribe(data => {
this.data = data;
this.title = data.teststring;
console.log(this.data);
});
}
Service
export class ContentService {
constructor(private http:Http) {
}
getData(): IObservable<{teststring:string}> { // where string can be some defined type
return http.get('assets/json/testjson.json')
.map(res => res.json() as {teststring:string});
}