I need to call the backend function in angular's component according to the timer.
I tried 2 options:
Option N1:
_api: Service;
constructor(api: Service){
this._api = api;
if (this.timeout != null) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.update();
}, 20000);
}
update()
{
this._api.GetData()….
}
The Result of this implementation does not run timer every N seconds, it calls update() function only once. But with this approach I can access the methods/properties of the this.api
OPTION N2:
_api: Service;
constructor(api: Service){
this._api = api;
setInterval(this.update, 2000);
}
update()
{
this._api.GetData()….
}
And the result is: for some reason it can't see the methods/properties of this._api.
I checked whether I can see them if I don't use setInterval and it seems that the problem is caused by setInterval.
Please advise how to arrange timer work of some method in Angular?
You are correct in using setInterval to run the chunk every n seconds, but you need to use arrow function notation to refer to the class member variables using this keyword. Try the following
constructor(api: Service) {
this._api = api;
setInterval(() => { this.update() }, 2000); // <-- use arrow function here
}
update() {
this._api.GetData()….
}
But what is the purpose of using an additional variable to refer to the injected service? You could directly refer to it.
constructor(api: Service) {
setInterval(() => { this.update() }, 2000);
}
update() {
this.api.GetData()….
}
Furthermore, I presume you are making some HTTP request in the GetData() function. In that case, it would be better to cancel any impending requests before triggering a new one in the interval.
dataSubscription: any;
constructor(api: Service) {
setInterval(() => { this.update() }, 2000);
}
update() {
if (this.dataSubscription) {
this.dataSubscription.unsubscribe(); // <-- unsubscribing cancels any impending requests
}
this.dataSubscription = this.api.GetData()….
}
How about interval function provided by rxjs?
class Service {
getData() {
return rxjs.of( Array(Math.floor(Math.random() * 5)).fill(0).map((pr, index) => ({id: index})))
}
}
class Component {
constructor(service) {
this.service = service;
this.observable = null;
this.ngOnInit = this.ngOnInit.bind(this);
}
ngOnInit() {
this.observable = rxjs.interval(3000)
.pipe(rxjs.operators.mergeMap(_ => this.service.getData()));
}
}
const service = new Service();
const component = new Component(service);
component.ngOnInit();
component.observable.subscribe(values => {
console.log(values)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>
Related
I have a service which gets returns some data at the page load,
getAllTurbinesStat(){
return this.http.get(this.url_stat_Turbines_all);
}
in my component I consume this service:
this.service.getAllTurbinesStat().subscribe( s => {
this.allStats.push(s);
});
allStat is an array, now every 3 minutes this function should be run to update data, should setInterval be in service or in my component? And how should I write it? Because the first time I don't need the setinterval, because once the page loaded first time, my data is updated.
You can try this.
First create one function in your component like this.
getAllTurbinesStat() {
this.service.getAllTurbinesStat().subscribe(s => {
this.allStats.push(s);
});
}
And then in from ngOnInit() or from constructor of your component use this.
this.getAllTurbinesStat();
setInterval(() => this.getAllTurbinesStat(), 180000);
You can use interval for that like below:
in you service
getAllTurbinesStat(){
return this.http.get(this.url_stat_Turbines_all);
}
getData(): {
return interval(3000).pipe(
switchMap( () => this.getAllTurbinesStat())
)
}
in your component
this.service.getData().subscribe( s => {
this.allStats.push(s);
});
You can use timer from rxjs.
import { timer } from 'rxjs';
/*
timer takes a second argument, how often to emit subsequent values
in this case we will emit first value after 0 second and subsequent
values every 3 minutes after
*/
const source = timer(0, 180000);
//output: 0,1,2,3,4,5......
const subscribe = source.subscribe(val => console.log(val));
For you case.
return timer(0, 180000).pipe(
flatMap( () => this.getAllTurbinesStat())
)
First call your function one time in the ngOnInit of your component then get on with setInterval.
ngOnInit() {
getAllTurbinesStat();
setInterval(getAllTurbinesStat(), 3000);
}
getAllTurbinesStat() {
return this.http.get(this.url_stat_Turbines_all);
}
I have an application that requires that I use non-Angular JavaScript for certain things. To trigger an action in the Angular component, I'm passing a down a callback to the non-Angular component. When the callback is triggered, an observable runs on the Angular component (doing an http call). This works but the only piece of the puzzle I'm having trouble with is getting the data returned from this observable passed back down to the non-Angular component somehow. My actual application is fairly complex so I've created a Stackblitz for a much more simplified version to make it easier to see what I'm doing.
This is tricky for me as the GET call in doStuff is async, so I can't just return the results. I'd have some ideas on how to work around this in a pure Angular app... but I'm not sure how to accomplish this when sharing data between an Angular component and a Non-Angular one.
app.component.ts:
export class AppComponent {
constructor(private http: HttpClient){}
doStuff() {
let randomNum = this.getRandomInt(2); // Simulate different http responses
this.http.get<any>(`https://jsonplaceholder.typicode.com/todos/${randomNum}`).subscribe(x => {
if (x === 1) {
// Here is where I want to share data with the non-Angular component
console.log(x.id);
} else {
// Here is where I want to share data with the non-Angular component
console.log(x.id);
}
});
}
ngOnInit() {
var x = new NonAngularComponent(this.doStuff.bind(this));
}
private getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max)) + 1;
}
}
NonAngularComponent.ts:
export class NonAngularComponent {
constructor(private onSave: () => void) {
this.init()
}
init() {
const newElement = document.createElement('button');
newElement.innerHTML = 'Click';
newElement.addEventListener('click', () => {
this.onSave(); // Works, but now I need to do something with the results of doStuff()
});
document.getElementById('foo').append(newElement);
}
}
Any suggestions would be much appreciated.
It would be better to return Observable from your doStuff() method and use tap operator if you want to have some side effect in Angular component:
doStuff() {
let randomNum = this.getRandomInt(2);
return this.http.get<any>(`https://jsonplaceholder.typicode.com/todos/${randomNum}`).pipe(tap(x => {
if (x === 1) {
// Here is where I want to share data with the non-Angular component
console.log(x.id);
} else {
// Here is where I want to share data with the non-Angular component
console.log(x.id);
}
}));
}
non-angular.component.ts
newElement.addEventListener('click', () => {
this.onSave().subscribe(res => {
// do whatever you want
});
});
Forked Stackblitz
I think that the easiest solution would be to simply have an instance of your NonAngularComponent inside the AppComponent
this.nonAngularComponent = new NonAngularComponent(this.doStuff.bind(this));
And in the callback simply call the method you want from the NonAngularComponent like so:
doStuff() {
let randomNum = this.getRandomInt(2);
this.http
.get<any>(`https://jsonplaceholder.typicode.com/todos/${randomNum}`)
.subscribe(x => {
if (x === 1) {
// Here is where I want to share data with the non-Angular component
// console.log(x.id);
this.nonAngularComponent.doSomething(x);
} else {
// Here is where I want to share data with the non-Angular component
// console.log(x.id);
this.nonAngularComponent.doSomething(x);
}
});
}
doSomething method:
public doSomething(result) {
console.log("Non-Angular component received result", result);
}
And console output:
Stackblitz: https://stackblitz.com/edit/angular-tddc7q?file=src%2Fapp%2FNonAngularComponent.ts
I follow this guide, and i try to do something similar at Unrelated Components: Sharing Data with a Service paragraph
Data Service:
#Injectable()
export class MyDataService{
private messageSource = new BehaviorSubject(null);
currentMessage = this.messageSource.asObservable();
constructor(private http: HttpClient) {
setInterval(() => { this.changeMessage(this.resultFromRestCall()); }, 10 * 1000);
}
changeMessage(message: object) {
this.messageSource.next(message);
}
resultFromRestCall(){
const json;
this.http.get<object>(myApiUrl).subscribe(res =>
json['data'] = res['data'] //this is an example
);
return json;
}
Component:
export class MyComponent implements OnInit {
constructor(private dataservice: MyDataService) {}
ngOnInit() {
this.dataservice.currentMessage.subscribe(
message => {this.handleVarChange(message); }
);
}
handleVarChange(message) {
console.log(message.data);
}
With this code i got "undefined" in handleVarChange log
Instead of calling this.handleVarChange(message); in subscribe I write console.log(message) i got my result correctly.
So, my question is if it's possible use the value coming from data service in some function of my component.
Thanks in advance
With:
resultFromRestCall(){
const json;
this.http.get<object>(myApiUrl).subscribe(res =>
// takes x amount of time to populate json
json['data'] = res['data'] //this is an example
);
// executed instantly after above request has been called
return json;
}
You are returning json before it has been populated, since the request is asynchronous.
Instead you can flip it around a bit, and call resultFromRestCall() first, and when you get the response, then call changeMessage():
setInterval(() => {
this.resultFromRestCall().subscribe((data) => {
this.changeMessage(data);
});
}, 10 * 1000);
where resultFromRestCall simply returns an observable:
resultFromRestCall(){
return this.http.get<object>(myApiUrl);
}
Also remember to clearInterval in OnDestroy!
DEMO
Omit the .data in handleVarChange:
Instead of
handleVarChange(message) {
console.log(message.data);
}
write
handleVarChange(message) {
console.log(message);
}
I am new to Angular(6). I am using setInterval function in a component. It is working but when I navigate to another route, setInterval continues to execute. Please help me to identify the reason.
//Calling it in ngOnit()
autosavedraftsolution() {
setInterval(() => {
console.log(this.draftSolutionForm);
if (this.solutionTitleValid) {
this.savedraftsolution();
}
}, this.autoSaveInterval);
}
//savedraftsolution()
savedraftsolution() {
console.log("saving..");
this.connectService.saveDraftSolution({
Title: this.draftSolutionForm.get('Title').value,
Product: this.draftSolutionForm.get('Product').value
} as Draftsolution).subscribe(draftsol => {
console.log("saved");
});
}
It keeps on showing me "saving.." and "saved" message in console.
You need to call clearInterval to stop it when your component unmounts:
this.intervalId = setInterval(...);
When your component is unmounting:
ngOnDestroy() {
clearInterval(this.intervalId);
}
Dominic is right. You have to clear the interval when the component is destroyed. You can make something like this
ngOnInit(){
this.saveInterval = setInterval(() => {}, 1000)
}
ngOnDestroy(){
clearInterval(this.saveInterval)
}
Make sure that your component implements OnInit and OnDestroy.
I wonder to know the best way of binding result of a promise which is as an injection to html tag using angular 2(I use ionic 2)...
As you know the main problem with async coding is loosing reference to the current object. It seems I should pass current object as a prameter to Promise function generator.
I searched internet for better solution but nothing I found! So is there any better approch?
Ionic 2 itself use observation and subscribe to do async proccess. But the major problem is that for existing functions which are not observable it couldn't work!
My approch:
Injectable class:
export class PromiseComponent {
doPromise = function (obj: any) {
return new Promise(function (resolve2) {
setTimeout(function () {
resolve2({ num: 3113, obj: obj });
}, 5000);
});
}
}
Call on click:
promiseVal = 0
doMyPromise() {
this.myPromise.doPromise(this).then(this.secondFunc);//UPDATED HERE
}
//UPDATED HERE
secondFunc = function (res) {
this.promiseVal = res.num
}
Html:
<div>{{promiseVal}} </div>
<button (click)="doMyPromise()">Do Promise</button>
If you want to consume a promise inside your component:
promiseVal = 0
doMyPromise() {
this.myPromise.doPromise().then((res) => {
this.promiseVal = res.num
});
}
And I don't know the reasoning behind your Service but it usually is like this (optional):
export class PromiseComponent {
doPromise() { //This method will return a promise
return new Promise(function (resolve2) {
setTimeout(function () {
resolve2({ num: 3113, obj: obj });
}, 5000);
});
}
}
After OP edited the post:
You can change this:
doMyPromise() {
this.myPromise.doPromise(this).then(this.secondFunc);//UPDATED HERE
}
to
doMyPromise() {
this.myPromise.doPromise(this).then(this.secondFunc.bind(this));//UPDATED HERE
}
As you know the main problem with async coding is losing reference to the current object
That's not true, the arrow function does not bind its own this therefore you don't need to send this to doPromise
export class PromiseComponent {
doPromise () {
return new Promise(function (resolve) {
setTimeout(function () {
resolve({ num: 3113 })
}, 5000)
})
}
}
promiseVal = 0
doMyPromise() {
this.myPromise.doPromise()
.then(res => {
this.promiseVal = res.num
})
}