I have a script that I would like to run on one component only. I have managed to achieve adding the script on the component but a couple of things happen that I'm not entirely sure how to resolve.
If I navigate to the component, the script is added to the DOM, but it isn't firing. If I refresh the page, it works
If I navigate away to another component and return, the script is added again, and it can keep building up
component.ts
import { Component, OnInit } from '#angular/core';
import { Renderer2, Inject } from '#angular/core';
import { DOCUMENT } from '#angular/platform-browser';
#Component({
selector: 'app-privacy',
templateUrl: './privacy.component.html',
styles: []
})
export class PrivacyComponent implements OnInit {
constructor(private _renderer2: Renderer2, #Inject(DOCUMENT) private _document) {
let s = this._renderer2.createElement('script');
s.type = `text/javascript`;
s.src = `../../assets/scripts/privacy.js`;
this._renderer2.appendChild(this._document.body, s);
}
ngOnInit() {
}
}
You need to add the onload (if you need to support IE make sure to also support onreadystatechange) handler to your script element which can call a function you want to execute when the script is finished loading.
To remove the script onNgDestroy, save a reference of createElement? on the Component and remove this in Destroy lifecycle hook.
import { Component, OnInit, OnDestroy } from '#angular/core';
import { Renderer2, Inject } from '#angular/core';
import { DOCUMENT } from '#angular/platform-browser';
#Component({
selector: 'app-privacy',
templateUrl: './privacy.component.html',
styles: []
})
export class PrivacyComponent implements OnInit, OnDestroy {
private s: any;
constructor(private _renderer2: Renderer2, #Inject(DOCUMENT) private _document) {
this.s = this._renderer2.createElement('script');
this.s.type = `text/javascript`;
this.s.src = `../../assets/scripts/privacy.js`;
this.s.onload = this.doneLoading;
this._renderer2.appendChild(this._document.body, this.s);
}
doneLoading () {
// do what you need to do
}
ngOnDestroy() {
// this removes the script so it won't be added again when the component gets initialized again.
this._renderer2.removeChild(this._document.body, this.s)
}
ngOnInit() {
}
}
Your approach in running this js file is wrong, you should do following to achieve this in the clean way:
Add your js file to the assets (for example assets/js/privacy.js)
Add file to the .angular-cli.json scripts
Now you can call your js functions from angular components if you declare them in the component
angular-cli.json
"scripts": [
"assets/js/privacy.js"
]
component.ts
import { Component, OnInit } from '#angular/core';
declare function myFunc(): any; // function from privacy.js
#Component({
selector: 'app-privacy',
templateUrl: './privacy.component.html',
styles: []
})
export class PrivacyComponent implements OnInit {
constructor() {
}
ngOnInit() {
myFunc(); // call it
}
}
Related
Below are the files of a library named posts-lib which makes http call inside posts.services.ts file and receives a list of posts and display them onto screen. It also consists a component named title.component.ts which is dependent on posts.services.ts and is responsible for displaying content on screen.
All of this works fine, but incase I want to move posts.service.ts folder out of the library and put it inside the app then how can I transfer the data from file which is outside of the library to the file title.component.ts which is dependent on it.
title.component.html
<h1>Testing titles api call</h1>
<ul>
<li *ngFor="let item of data">{{item.title}}</li>
</ul>
title.component.ts
import { Component, OnInit } from '#angular/core';
import { PostsService } from '../posts.service';
#Component({
selector: 'lib-tilte',
templateUrl: './tilte.component.html',
styleUrls: ['./tilte.component.css']
})
export class TilteComponent implements OnInit {
data: any;
constructor(private postData: PostsService) { }
ngOnInit() {
this.postData.getPosts().subscribe((result) => {
console.warn("reult",result);
this.data = result;
})
}
}
posts-lib.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'lib-posts-lib',
template: `
<p>
posts-lib works!
</p>
`,
styles: [
]
})
export class PostsLibComponent implements OnInit {
constructor() { }
ngOnInit(): void {
}
}
posts-lib.module.ts
import { NgModule } from '#angular/core';
import { PostsLibComponent } from './posts-lib.component';
import { TilteComponent } from './tilte/tilte.component';
import { HttpClientModule } from "#angular/common/http";
import { CommonModule } from '#angular/common'
#NgModule({
declarations: [
PostsLibComponent,
TilteComponent
],
imports: [
HttpClientModule,
CommonModule
],
exports: [
PostsLibComponent,
TilteComponent
]
})
export class PostsLibModule { }
posts.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from "#angular/common/http";
#Injectable({
providedIn: 'root'
})
export class PostsService {
url = "https://jsonplaceholder.typicode.com/posts";
constructor(private http: HttpClient) { }
getPosts() {
return this.http.get(this.url);
}
}
public-api.ts
export * from './lib/tilte/tilte.component';
export * from './lib/posts-lib.service';
export * from './lib/posts-lib.component';
export * from './lib/posts-lib.module';
export * from './lib/posts.service';
Ignoring all the issues the commenters are making - all valid - it sounds like you want to just remove the dependency on the service.
Or not, actually.
Yay, options!
Remove usage of the service
Just turn the component around from getting its own data, to being given its data. I.e. #Input.
Still with #Input, but instead, input the service itself rather than the values.
So either:
#Input() public data: any;
or
#Input() public set service(value: PostsService) {
this.postsService = value;
this.getData();
}
private getData(): void {
this.postsService.getPosts().subscribe(...);
}
Either way if you're moving the service out and no longer expecting the service and component to work as a functional pair within a system, you have to extract the component and feed it information instead with #Inputs.
Whether that's just feeding it the data from [a wrapper] service, or feeding it the service itself from wherever it now lives, you still need to give it to it.
I want to send the value from one component to another, they are not related so all solutions are saying that I must use shared service to do that. But these services are using templates (if I'm right). Is there a way to do this sharing without services?
I want to send the BMI value from homepage.component.ts to result.component.ts.
homepage.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
constructor() { }
myHeight!:number;
myWeight!:number;
bmi!:number;
ngOnInit(): void {
}
onGenerate( height:string,width:string){
this.myHeight = +height;
this.myHeight=Math.pow(this.myHeight/100,2);
this.myWeight = +width;
this.bmi=this.myWeight/this.myHeight
console.log(this.bmi); //this is the calculated value to send
}
}
result.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-result',
templateUrl: './result.component.html',
styleUrls: ['./result.component.css']
})
export class ResultComponent implements OnInit {
constructor() { }
//I want to get the bmi here
ngOnInit(): void {
}
}
There are Two ways to communicate between unrelated components in angular:
1 - Through services, you have to understand where to inject it, in your case I think it should be injected in root, so try this with your service ( follow this tutorial to implement your service, just add my code instead of theirs )
#Injectable({
providedIn: 'root',
})
2 - Through a store ( a lot of boilerplate coding, to use if you have complexe states to keep synchronized through the whole app, by the way the store is basically a service )
If your components are not related then you can create a shared service between them. Then, you need to use dependency injection to communicate between these components. So, there is a great Angular tutorial which describes how to do it.
The service code would look like this:
#Injectable()
export class FooService {
constructor( ) { }
private yourData;
setData(data){
this.yourData = data;
}
getData(){
let temp = this.yourData;
this.clearData();
return temp;
}
}
and sender component:
import { Router } from '#angular/router';
import { FooService} from './services/foo.service';
export class SenderComponent implements OnInit {
constructor(
private fooService: FooService,
private router:Router) {}
somefunction(data){
this.fooService.setData(data);
this.router.navigateByUrl('/reciever');//as per router
}
}
and subscriber:
import { Router } from '#angular/router';
import { TransfereService } from './services/transfer.service';
export class RecieverComponent implements OnInit {
data;
constructor(
private fooService: FooService){
}
ngOnInit() {
data = this.transfereService.getData();
console.log(`data: `, data)
}
}
Solution: To pass the data from one component to another we can store it in a session storage or a local storage and then access it in other components from that storage. Here I have provided a sample code using local storage for your reference.
homepage.component.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-homepage',
templateUrl: './homepage.component.html',
styleUrls: ['./homepage.component.css']
})
export class HomepageComponent implements OnInit {
constructor() { }
myHeight!:number;
myWeight!:number;
data:string='';
bmi!:number;
ngOnInit(): void {
}
onGenerate( height:string,width:string){
this.myHeight = +height;
this.myHeight=Math.pow(this.myHeight/100,2);
this.myWeight = +width;
this.bmi=this.myWeight/this.myHeight;
this.data=localStorage.setItem('bmi',this.bmi);
console.log(this.bmi); //this is the calculated value to send
}
}
resultcomponent.ts:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-result',
templateUrl: './result.component.html',
styleUrls: ['./result.component.css']
})
export class ResultComponent implements OnInit {
data:any;
constructor() { this.data=localstorage.getItem('bmi')}
//Access the bmi using the data variable here
ngOnInit(): void {
}
}
I have global.js script file and need to launch InitSwiper() function when route changes to '/home', but can't find how to track router in script file or launch function through home.component.ts
import { Component, OnInit } from '#angular/core';
declare var global: any;
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
ngOnInit() {
global.initSwiper();
}
}
global.js
$(function () {
"use strict";
$(window).load(function(){
pageCalculations();
$('#loader-wrapper').fadeOut();
$('body').addClass('loaded');
initSwiper();
});
...
})
If you are using CLI, you need to include that file in .angular-cli.json file inside "scripts" array.
if you want to call a function from that file in home.component.ts only, then you can declare as below
declare var global:any;
and then on
ngOnInit(){
global.InitSwiper();
}
Some have suggested guards, but it's overkill if you don't need to delay or prevent the route from being loaded.
If you import router in your constructor you can actually subscribe it like so:
import { Router, NavigationEnd } from '#angular/router';
constructor(private next: Router) {
next.events.subscribe((route) => {
if (route instanceof NavigationEnd) {
console.log(route.url);
}
});
}
In the example above it should print out the current route.
You can create a service and have your router call it in the canActivate once it goes to the required route like so. This will let you handle anything before the component gets loaded
router.module.ts
...
import {myService} from '../services/myService.service'
export const routes: Routes = [
{path: '/home', component: HomeComponent, canActivate:[myService]}
]
...
myService.service.ts
import { Injectable } from '#angular/core';
import { CanActivate, Router } from '#angular/router';
#Injectable()
export class myService implements canActivate{
canActivate{
//execute initSwiper here
if(/*success?*/){
return true;
}
else{
//redirect?
}
constructor(
private _router: Router) { }
I recently ran into a problem and can't really figure out what's wrong with my code at this point, hopefully someone of you can help me.
All I am trying to do is changing the value of my BehaviorSubject with a function but it isn't working out.
chat.service.ts
import { Injectable } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class ChatService {
chatId = new BehaviorSubject<number>(0);
constructor() {
this.chatId.next(1);
}
changeChatId(chatId: number) {
console.log(chatId);
this.chatId.next(chatId);
}
}
So the subscribers get the default as well as the changed chatId from the constructor. But as soon as I try to change it with the changeChatId function nothing happens at all. The right id's get passed into the function I already debugged that but the line this.chatId.next(chatId) doesn't seem to do anything.
ADD
These are the other components the service is currently used in.
chat-message-list
import { Component, OnInit, Input} from '#angular/core';
import { ChatService } from "../../../shared/services/chat.service";
#Component({
selector: 'app-chat-message-list',
templateUrl: './chat-message-list.component.html',
styleUrls: ['./chat-message-list.component.css'],
providers: [ChatService]
})
export class ChatMessageListComponent implements OnInit {
chatId: number;
constructor(private chat: ChatService) { }
ngOnInit() {
this.chat.chatId.subscribe(
chatId => this.updateMessageList(chatId)
);
}
}
chat-item
import { Component, OnInit, Input} from '#angular/core';
import { User } from '../../../shared/models/user.model';
import { ChatService } from '../../../shared/services/chat.service';
#Component({
selector: 'app-chat-list-item',
templateUrl: './chat-list-item.component.html',
styleUrls: ['./chat-list-item.component.css'],
providers: [ChatService]
})
export class ChatListItemComponent implements OnInit {
#Input()
user: User;
constructor(private chat: ChatService) { }
ngOnInit() {
}
onChatItemSelected(){
this.chat.changeChatId(this.user.id);
}
}
You need to make your ChatService a singleton (shared) service. Add it to the providers of your ngModule. This allows all the components that use the ChatService to share the same service instance.
#NgModule({
providers: [ChatService]
})
And remove it from your components providers. When you are adding it to your components providers, that component gets its own instance of ChatService which can not be used by other components.
i created a new component in angular 2 with this:
ng g component todos
So it created the new component, I went to the component and I noted that I had a new folder with the files:
todos.component.css, todos.component.html, todos.component.spec.ts, todos.component.ts
Then I openened todos.component.ts and it had:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-todos',
templateUrl: './todos.component.html',
styleUrls: ['./todos.component.css']
})
export class TodosComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
Then I put the new second line because I am learning with a tutorial:
import { Component, OnInit } from '#angular/core';
import { TodosComponent } from './todos/todos.component';
#Component({
selector: 'app-todos',
templateUrl: './todos.component.html',
styleUrls: ['./todos.component.css']
})
export class TodosComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
When I did that and I ran the server it showed me this:
Failed to compile.
C:/angular2/proyecto/src/app/todos/todos.component.ts (2,10): Individual declarations in merged declaration 'TodosComponent' must be all exported or all local.
I'd like to know what is it bad? why does it show that error?
Thanks!
You are importing the class into it's own file.
No need to import your own component, you should import it in other files, where you use it.