I'm working on an application that has a session timeout after 30 mins of inactivity. I have a new requirement to pop up a message asking users if they'd like to keep their session active, a couple mins before they're automatically logged out.
Right now the session is managed in what I think is a pretty unorthodox manner, and I need to try to work with what's already there. There's a service used by the App Module called context.service (injected as a provider) that uses a setTimeout to determine when 30 mins of inactivity has expired.
Given that I need access to that countdown, I wanted to create a mirrored timeout that executes 2 mins earlier and fires the modal, asking the user if they want to keep their session open. After injecting NgbModal into the ContextService I receive a circular reference error which seems quite reasonable. It seems a little crazy to try to populate a modal on the DOM using a provider, but I'm not sure what a viable alternative is.
Here's the current state as it exists (with the circular reference error):
// ...
import { SessionExpirationWarning } from '../components/session-expiration-warning/session-expiration-warning.component';
// ....
constructor(
private _http: HttpClient,
private _injector: Injector,
private modalSvc: NgbModal
) {
// ...
}
// ...
setSessionTimeout() {
if (this.appConfig === null) { return; }
clearTimeout(this._timeoutId);
clearTimeout(this.timeoutWarning);
const sessionTimeOutConfig = this.appConfig.SessionTimeoutMinutes;
const SessionTimeoutMinutes = sessionTimeOutConfig === undefined ? 5 : sessionTimeOutConfig;
const sessionWarningMinutes = 2;
this._timeoutId = setTimeout(() => {
this.sessionExpired();
}, SessionTimeoutMinutes * (60 * 1000));
this.timeoutWarning = setTimeout(() => {
if (!this.warningIsActive) {
const timeOutWarningModal = this.modalSvc.open(SessionExpirationWarning);
timeOutWarningModal.result.then((modalResponse) => {
if (modalResponse === true) {
this.keepAlive(null);
}
});
}
}, sessionWarningMinutes * (60 * 1000));
}
The this.timeoutWarning was my attempt at hacking together a solution.
What you could do is to have an Observable that emits when the warning popup should be displayed:
import { timer } from 'rxjs/observable/timer';
// ...
public sessionWarningTimer$ = new Subject();
// ...
setSessionTimeout() {
// ...
timer(sessionWarningMinutes * 60 * 1000).subscribe(this.sessionWarningTimer$);
}
In a component (e.g. your AppComponent) you could then subscribe to sessionWarningTimer$:
private destroyed$ = new Subject();
ngOnInit() {
this
.contextService
.sessionWarningTimer$
.takeUntil(this.destroyed$)
.subscribe(() => this.displaySessionWarning());
}
ngOnDestroy() {
this.destroyed$.next();
}
displaySessionWarning() {
// your display code here
}
Like this, you can avoid any UI code in your service and rather focus on the warning logics.
Related
I have a network of multiple “nodes” running Meteor v1.5.4.2 (due to dependencies). Each of these nodes is supposed to be able to communicate with the others to fetch statistics etc. This is done using Meteors ddp-client server side on the nodes that should get information from the others.
This seemingly worked well, but when we started provoking it with a lot of changes in the network (meaning a lot of connections come and go) the memory gradually builds up until it freezes up and belly flops. I have limited experience resolving memory leaks, but by looking at heap snapshots, I found that there’s a buildup of objects called “Connection” (pasted below). Under strings I also find a lot of strings containing the certs and CRL’s used in the DDP connection, leading me to believe theres an issue with my code involving the connection handling. Ive tried to list the highlights below, removing a lot of minor logic involved.
I am at a little bit of a loss as to further approach this, so any suggestions, thoughts or ideas would be most welcome.
Thanks in advance.
Heres a compressed run down of how its connected
if(Meteor.isServer) {
connectionHandler = new DDPConnectionHandler();
Meteor.setInterval( () => connectionHandler.checkNodeConnections(), 5000);
}
export const DDPConnectionHandler = function() {
this.connections = [];
this.checkNodeConnections = () => {
// Logic to add or remove the node connections in this.connections
// Looping pr. node to handle
const node = {...} // Details of the node to add/remove
// Add new conncetion
this.connections.push( new DDPConnection(node) );
// Remove connection
const index = currentConnections.indexOf(node.id);
this.connections[index].disconnect();
this.connections.splice(index, 1);
};
}
export const DDPConnection = function(node) {
let self = this;
// setting up variables to use, pw, user, url ... etc.
this.connection = DDP.connect(url, { /* certs etc. for SSL */ });
this.connection.call("login", {/* login details */}, (error, result) => {
if( !error ) {
// Wrap in timeout to space out the stats calls
Meteor.setTimeout( () => { self.initNodeStats(); }, randomNumber );
} else { /* No luck */ }
});
this.disconnect = () => {
this.connection.disconnect(); // also tried with .close()
};
this.subscribe = (collection) => {
// function to fetch other data
};
// Initialize and start recieving default basis ndoestats from current external nde
this.initNodeStats = () => { this.getStats(); };
this.getStats = () => {
self.connection.call('getStats', {}, (error, result) => {
if( error ) { /* No luck */
} else if ( result ) { /* Pass data to handlers */ }
});
}
}
Connection
_stream::ClientStream
__proto__::Object
_outstandingMethodBlocks::Array
__flushBufferedWrites::()
map::system / Map
_methodInvokers::Object
properties::(object properties)[]
_bufferedWritesFlushAt::system / Oddball
_bufferedWritesFlushHandle::system / Oddball
_lastSessionId::system / Oddball
_retryMigrate::system / Oddball
_userId::system / Oddball
_version::system / Oddball
_versionSuggestion::system / Oddball
onReconnect::system / Oddball
_supportedDDPVersions::Array
_userIdDeps::Tracker.Dependency
_bufferedWrites::Object
_documentsWrittenByStub::Object
_methodHandlers::Object
_methodsBlockingQuiescence::Object
_serverDocuments::Object
_stores::Object
_subsBeingRevived::Object
_subscriptions::Object
_updatesForUnknownStores::Object
_afterUpdateCallbacks::Array
_messagesBufferedUntilQuiescence::Array
_resetStores::system / Oddball
Update, after digging some more.
I seem to be getting a buildup of "methods" in the "_outstandingMethodBlocks" attribute on the Connection objects. Which is defined on line 129 here:
https://github.com/meteor/meteor/blob/release-1.5.4.2/packages/ddp-client/livedata_connection.js#L129
Maybe theres some timeout setting I could use to stop them from being stored there?
Let's say we have this global const:
const isSignedIn = fromPromise(fetch('/api/is-signed-in'))
.pipe(throttleTime(1000), shareReply(1));
After page load, several components will subscribe to this at the same time:
isSignedIn.subscribe(() => console.log('do 1st'));
isSignedIn.subscribe(() => console.log('do 2nd'));
isSignedIn.subscribe(() => console.log('do 3rd'));
The above will only call the API once, however i need it to call the API again (ie after 1 second) if another component subscribes to it.
isSignedIn.subscribe(() => console.log('button press'));
How do i that using RxJS?
I think this is what you want:
A pipeable operator (declare globally somewhere and import it)
export const refreshAfter = (duration: number) => (source: Observable<any>) =>
source.pipe(
repeatWhen(obs => obs.pipe(delay(duration))),
publishReplay(1),
refCount());
Then use it like this:
data$ = fetch('/api/is-signed-in').pipe(refreshAfter(5000)); // refresh after 5000 ms
Note: You actually asked for this:
i need it to call the API again (ie after 1 second) if another component subscribes to
it.
Not quite sure this is what you really meant. I think what you really meant was - you want the data to be refreshed for all components currently subscribed after an expiry time. Anyway my answer sends the new value to all listeners. If you really want what you originally said you'd need to add some kind of alternative repeat trigger.
But if this is for a global constant - the above is what I'm using for the same scenario.
Note: I haven't actually tested the handling of an error condition when the item is repested, but I think the error will propagate to all listeners.
If we reimplement ShareReplay so it:
- will never unsubscribe from source even if it have no more subscribers (remove refCount, potential memory leak).
- accept rerunAfter argument, time passed from last subscribe to source.
import {Subject, of, Observable, ReplaySubject, Subscriber} from 'rxjs';
import {pluck, shareReplay, tap, delay} from 'rxjs/operators';
function shareForeverReplayRerun<T>(bufferSize: number, rerunAfter: number) {
let subject;
let subscription;
let hasError = false;
let isComplete = false;
let lastSubTime = 0;
return source => Observable.create((observer: Subscriber<T>) => {
if (!subject || hasError || (Date.now() - lastSubTime) >= rerunAfter) {
lastSubTime = Date.now();
hasError = false;
subject = new ReplaySubject<T>(bufferSize);
subscription = source.subscribe({
next(value) { subject.next(value); },
error(err) {
hasError = true;
subject.error(err);
},
complete() {
isComplete = true;
subject.complete();
},
});
}
const innerSub = subject.subscribe(observer);
// never unsubscribe from source
return () => {
innerSub.unsubscribe();
};
})
}
const source = of('Initial').pipe(
tap(()=>console.log('COMPUTE')),
delay(200),
shareReplayRerun(1, 1000),
);
source.subscribe(console.log.bind(null, 'syncI:'));
source.subscribe(console.log.bind(null, 'syncII:'));
setTimeout(()=>source.subscribe(console.log.bind(null, 'after500:')), 500);
setTimeout(()=>source.subscribe(console.log.bind(null, 'after900:')), 900);
setTimeout(()=>source.subscribe(console.log.bind(null, 'after1500:')), 1500);
as output we have:
COMPUTE
syncI: Initial
syncII: Initial
after500: Initial
after900: Initial
COMPUTE
after1500:Initial
EDITED: The answer is wrong. BufferSize is how long the last N events are replayed. After this the stream is completed.
signature: shareReplay(
bufferSize?: number,
windowTime?: number,
scheduler?: IIScheduler
):Observable
#param {Number} [bufferSize=Number.POSITIVE_INFINITY] Maximum element count of the replay buffer.
#param {Number} [windowTime=Number.MAX_VALUE] Maximum time length of the replay buffer in milliseconds.
Try to add 1000 as second argument to shareReply:
const isSignedIn = fromPromise(fetch('/api/is-signed-in'))
.pipe(throttleTime(1000), shareReplay(1, 1000));
shareReplay.ts - be care of refCount-- on unsubcribe as it can trigger additional requests.
I want to track how much time user is taking in completing a particular action (including server response time and render time(DOM related changes )) in website.
I have tried it in Angular framework. To do it, I am thinking of recording the time when user started the action and I want to note the time when the action is completed. As a developer, I will know when user started the activity and when user finish the action like search, filter, edit, add, delete etc. So, we can take the difference b/w them. But to note every action, we have to write code in every part of the app. Can we create a plugin so that we can use it everywhere instead of writing same code everywhere to track the time of user. Any approach to create it? Or is there any tool available to achieve this feature?
Would something like this help?
#Injectable({provideIn: 'root'})
export class TrackingService {
private cache: {[id: number]: {description: string, time: number}} = {};
private id: number = 0;
public startTracking(actionDescription: string): number{
const id = ++this.id;
this.cache[id] = { description: actionDescription, time: new Date().getTime() };
return id;
}
public stopTracking(actionId: number){
const data = this.cache[actionId];
if(data){
const elapsed = new Date().getTime() - data.time;
// ...
// Do something with your 'elapsed' and 'data.description'
// ...
delete this.cache[id];
return {...data, elapsed: elapsed};
}
throw `No action with id [${actionId}] running! `;
}
}
Ad then anywhere you need to track an action:
private actionId: number;
constructor(private trackingService: TrackingService){}
startAction(){
this.actionId = this.trackingService.startTracking('Description');
}
stopAction(){
const trackingResult = this.trackingService.stopTracking(this.actionId);
}
You can automate the tracking in some places, for example for routing:
// app.module.ts
private routeChangeSubscription: Subscription;
private configLoadActionId: number;
private navigationActionId: number;
constructor(private router: Router, private trackingService: TrackingService){
this.routeChangeSubscription = router.events.subscribe((event: Event) => {
if (event instanceof RouteConfigLoadStart) {
this.configLoadActionId = this.trackingService.startTracking('configLoad');
}
else if (event instanceof RouteConfigLoadEnd) {
const result = this.trackingService.stopTracking(this.configLoadActionId);
// ... process the result if you wish
}
else if (event instanceof NavigationStart) {
this.navigationActionId = this.trackingService.startTracking('navigation');
}
else if (event instanceof NavigationEnd) {
const result = this.trackingService.stopTracking(this.navigationActionId);
// ... process the result if you wish
}
});
}
Or for HTTP requests:
// http-tracking.interceptor
export class HttpTrackingInterceptor implements HttpInterceptor {
constructor(private trackingService: TrackingService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const actionId = this.trackingService.startTracking('HTTP request');
return next.handle(req.clone()).pipe(
tap(r => this.trackingService.stopTracking(actionId))
);
}
}
// app.module.ts
#NgModule({
// ... other module stuff
providers: [
// ... other providers
{
provide: HTTP_INTERCEPTORS,
useClass: HttpTrackingInterceptor,
multi: true,
deps: [TrackingService]
}
]
})
export class AppModule { ... }
You can easily extend the TrackingService to return Promises or Observables or whatever else, in case you prefer that...
Hope this helps a little :-)
Can we create a plugin so that we can use it everywhere instead of
writing same code everywhere to track the time of user. Any approach
to create it? Or is there any tool available to achieve this feature?
It's a very important Feature Request by many. So, I write a detailed, working and simple solution on the subject here.
#himanshu-garg You are requesting a feature already created for this workflow. It's a plugin you can include in any website. It's none other than activity tracking in timeonsite.js
Look at the following code,
<head>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/timeonsite/1.2.0/timeonsitetracker.js"></script>
<script>
var config = {
// track page by seconds. Default tracking is by milliseconds
trackBy: 'seconds',
callback: function(data) { /* callback denotes your data tracking is real-time */
console.log(data);
var endPointUrl = 'http://example.com' //Replace with your actual backend API URL http://localhost/tos
if (data && data.trackingType) {
if (data.trackingType == 'tos') {
if (Tos.verifyData(data) != 'valid') {
console.log('Data abolished!');
return;
}
}
// make use of sendBeacon if this API is supported by your browser.
if (navigator && typeof navigator.sendBeacon === 'function') {
data.trasferredWith = 'sendBeacon';
var blob = new Blob([JSON.stringify(data)], {type : 'application/json'});
navigator.sendBeacon(endPointUrl, blob);
}
}
}
};
var Tos;
if (TimeOnSiteTracker) {
Tos = new TimeOnSiteTracker(config);
}
</script>
</head>
Then, when the user clicks on a specific action in the site, for example "edit the post" or "click on the create post",
You just initiate the Tos.startActivity() API like,
Tos.startActivity({actionPerfomed: 'Edit a post'});
Then when the user completes the edit or create post actions and when he finally clicks the "save/submit" button, you trigger the Tos.endActivity() API like,
Tos.endActivity({customData: 'custom data if any here...'});
You'll see following object directly saved into your table,
{
TOSId: 585872449448,
TOSSessionKey: "14802525481391382263",
TOSUserId: "anonymous",
title: "Test application - TimeOnSiteTracker",
URL: "http://example.com/post/nature-is-beautiful/edit.php",
activityStart: "2021-11-27 13:20:46.707",
activityEnd: "2021-11-27 13:20:50.213",
timeTaken:4,
timeTakenByDuration: "0d 00h 00m 04s"
timeTakenTrackedBy: "second",
trackingType: "activity",
actionPerfomed: "Edit a post", //optional fields
customData: "custom data if any here..." //optional fields
}
As you can see, the actions
"Edit/Create post" is captured
"timeTaken" is captured in seconds/milliseconds depending upon configuration
"type:activity" is captured
"activityStart" is captured
"activityEnd" is captured
"TOSUserId" // who does the action along with TOSSessionKey to uniquely identify the session.
What else you need? Since it's stored in SQL DB table, you can do analysis/reporting queries yourself and take it to top-level management for decisions. The same is the case for NoSQL as well. Timeonsite.js is supporting both RDBMS and NoSql DB types.
On top of it, 1.Minimize tab, 2.Inactive tab and 3.Switch tab's idle time are all computed and ignored automatically by the tracker itself.
This tracker can be plugged-in in any library Angular, React, Jquery etc. since it's plain vanilla JS library.
Let me know if you need more input on the subject. I can assist you on this.
You have to write a simple Event Tracker in your client code. Since I don't know which events you want to track, I'll provide the solution for a general case.
Also, you'll have to manually trigger the start and stop tracking.
EventTracker = {
trackedEvents: {},
start: function(key) {
var startTime = new Date();
this.trackedEvents[key] = {
start: startTime
}
},
stop: function(key) {
var endTime = new Date();
this.trackedEvents[key]['duration'] = (endTime - this.trackedEvents[key]['start']) / 1000 + 's';
this.trackedEvents[key]['end'] = endTime;
},
}
// Use EventTracker everywhere to track performance
// Example:
EventTracker.start('search_track'); // User searches, start tracking.
setTimeout(function() {
EventTracker.stop('search_track'); // Records fetched after 5 seconds. Stop tracking.
console.log(EventTracker.trackedEvents);
}, 5000);
You can track all events according to your need. For server response, use: EventTracker.start('search_ajax_track') when you make the request and stop the tracking when you get the response.
You can modify above code to measure other parameters according to your requirements.
I am going to recommend you use custom Google Analytics events. In particular User Timings. This allows you to log specific timings on your webpage, you can log with your own labels and categories.
To quote the documentation:
User timings allow developers to measure periods of time using the
analytics.js library. This is particularly useful for developers to
measure the latency, or time spent, making AJAX requests and loading
web resources.
I have some sample code below, this just hooks into clicks, and will get a descriptor from attribute data-name - if not available will just log as 'Anonymous Click' - you can customise this to not track unmarked items. You can also hook into ajax calls and other notable events, without knowing your specific requirements it's hard to give further examples.
Example markup helper to lock click events.
<button data-name="Foo"/>
The below code does the logging, note that it logs using window.performance.now() - which will return the time from when the page was loaded in milliseconds. This will allow you to generate a timeline of user interactions as opposed to getting raw time spent on a single task, which by the way Google Analytics reports can calculate for you.
(function($, Analytics) {
init_hooks();
function init_hooks() {
$('body').on('click', track);
}
function track(e) {
// Get a name to record this against
var name = e.target.data(name) || "Anonymous Click";
// Time since page loaded
var time = window.performance.now()
Analytics('send', {
hitType: 'timing',
timingCategory: 'Front End Intereactions',
timingVar: name,
timingValue: time
});
}
})(jQuery, ga)
Find out more look at the docs.
You could instrument your code with OpenTracing for Js.
You will need to add a request in your transaction start and end.
Also a OpenTracing server to receive request from the browser.
I am new to react-native. I wrote stop watch but the problem is that when I go to another page and then come back again the stop watch stop working. I want the stop watch keep working until the user press the stop button.I know, I should use global variable but I dont Know how to use it.here is my code:
export default class Record extends Component {
constructor(props) {
super(props);
this.state = {
stopwatchStart: false,
stopWatchTime: '00:00:00'
};
toggleStopwatch() {
if (!this.state.stopwatchStart) {
startDate = new Date();
startTime = startDate.getTime();
that = this;
setInterval(function () {
var date = new Date();
time = (date.getTime() - startTime) / 1000;
hour = parseInt(time / 3600);
timeAgo = parseInt(time % 3600);
min = parseInt(timeAgo / 60);
second = parseInt(timeAgo % 60);
that.setState({
stopWatchTime: (hour + ':' + min + ':' + second)
})
}, 1000);
}
this.setState({ stopwatchStart: !this.state.stopwatchStart });}
I think you should use redux for handling this...
https://redux.js.org/
I'm trying to understand what do you need, so I have the following question, do you want the interval to continue running after you change the screen?, because the components do not work that way.
You must follow the life cycle of the components (https://reactjs.org/docs/react-component.html), therefore, after you change the screen of the "Record" component, it will be unmount and the interval will destroy.
You may describe a little more what do you want to do with the interval, in case you want to use it in more than one component.
I still don't understand what you mean by view but probably you are opening another app or taking app to background. To persist data in your React Native application you should use AsyncStorage. In future you might want to use Sqlite, Redux persist or something else like these ones to keep your data persistent.
You can set this before app goes to background.
try {
await AsyncStorage.setItem('timeKey', myTimeVar.toString());
} catch (error) {
// Error saving data
}
And by using your key you can get the value of latest
try {
const value = await AsyncStorage.getItem('timeKey');
if (value !== null){
// We have data!!
console.log(value);
}
} catch (error) {
// Error retrieving data
}
You don't have to run timer in background by doing simple extraction (appClosedTime-currrentTime+timeWastedInFirstSession) you can achieve your goal.
I have array of object with following data :
[
{
"name":"Uber",
"points":20,
"nodeName":"C=GB,L=London,O=Uber",
"port":10007,
"nodeType":"merchant"
},
{
"name":"Starbucks",
"points":20,
"nodeName":"C=US,L=New York,O=Starbucks",
"port":10010,
"nodeType":"merchant"
},
{
"name":"KFC",
"points":20,
"nodeName":"C=US,L=New York,O=KFC",
"port":10013,
"nodeType":"merchant"
}
]
I want to loop through this array and show determinate progress bar with animation and also should see points increasing in view shown in below image.for points attribute and want to show it one after another. i.e first for Uber progress bar displayed which will load till 100% for points of uber. and so on for starbucks and then for KFC.
I have tried below code ,where res is my above array:
res.forEach((v, i) => {
Observable.timer(100).subscribe((i) => {
let interval = Observable.interval(100).subscribe((i) => {
this.percentageCom = (this.points / this.PointsAdded) * 100;
if (this.percentageCom === 100) {
// this.isHidden = true;
interval.unsubscribe();
}
})
});
});
let timer = Observable.interval(100).subscribe(() => {
this.points++;
if (this.points === 60) {
timer.unsubscribe();
// this.router.navigate(['/main/dashboard']);
}
});
and HTML :
<div class="vendor">{{merchantName|| 'Uber'}}</div>
<mat-progress-bar mode="determinate" value="{{percentageCom }}"></mat-progress-bar>
But above code not display my progress bar one after another as those things are asynchronous,some weird output being shown i.e. simultaneously displaying progress bar .
Is there any way to show above loader one after another for each element of array ?
UPDATED
My Use case is as follow :
From service response I get Array of object as mention above
Lets consider first item of that array of object. It has points 20; so in my View ,points should increase from 0 to 20(just like counter)
So while this point increment happens till 20, I want to show percentage Progress bar for Uber in this case which will run full 100%.
Once above 2 points complete for one object i.e. uber same should happen for next item in array of object.
Above are the steps that i want to implement. But as interval and timer are async, i am not able to run it one after another by looping abpve object.
I have messed up code there in timer and interval...somehow I couldnt get throuugh it !
I don't exactly find the purpose of mocking that loading behavior, but I think that you are looking for something like this code snipped. I think too, and the example shows, that you can remove the points variable.. but I am not sure because I did not understand 100% the context on where you are.
const res = [
{
"name":"Uber",
"points":20,
"nodeName":"C=GB,L=London,O=Uber",
"port":10007,
"nodeType":"merchant"
},
{
"name":"Starbucks",
"points":20,
"nodeName":"C=US,L=New York,O=Starbucks",
"port":10010,
"nodeType":"merchant"
},
{
"name":"KFC",
"points":20,
"nodeName":"C=US,L=New York,O=KFC",
"port":10013,
"nodeType":"merchant"
}
];
Rx.Observable.from(res)
.map( (value) => {
return Rx.Observable.interval(100)
.scan((acc, curr) => acc + 1)
.take(value.points+1)
.map(currentPoints => {
const percentage = (currentPoints / value.points) * 100;
return {arrayItem: value, percentage: percentage}
})
})
.concatAll()
.subscribe((data) => {
console.log(data.arrayItem.name + ': ' + data.percentage);
});
<script src="https://npmcdn.com/#reactivex/rxjs#5.0.0-beta.8/dist/global/Rx.umd.js"></script>
Edit: I edited the solution, now is in serie.
Explanation:
First we convert our array into an stream. 'Observable.from(res)'.
For every item on the array, now in the stream, we map it into an Observable.interval(100)
We take that Observable interval and we count the times it emits with scan and we finish it taking only as items as point the array item have.
After that we map an return the value with its current percentage.
The concatAll() operator just concatenates the observable sequence we have.
Finally the subscribe method only shows a console log of the result
I'm not sure, but I think you are doing an interval in the wrong place. You should do this on http get, then your subscribers will catch changes on observables with intervals. What you are looking for probably is called polling request and is high frequencies here
helpful article, related issue
example component.ts file:
import { Component, OnInit } from '#angular/core';
import { Service } from './service';
#Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.css']
})
export class MainComponent implements OnInit {
constructor(private service: Service) { }
ngOnInit() {
this.service.getNewValue()
.subscribe((res) => console.log(res));
}
}
example service.ts file:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs'; // ! Dont forget to Import Observable !
#Injectable()
export class Service {
constructor(private http: Http) { }
getNewValue = () => {
return Observable
.interval(1000)
.flatMap((i) => this.http.get("http://yourhost.com/api"))
}
}
didn't test it but should fix your problem
You are creating a new timer for each item. I don't think that is what you want.
I think you want something like this:
res.forEach((v, i) => {
//Add the point here because this is where your processing happens I guess?
this.points++;
});
//Move it outside of your loop
$timer = Observable.timer(100).subscribe((i) => {// $timer as a local variable. 1000/60 might be better as it will refresh for every frame.
this.percentageCom = (this.points / this.PointsAdded) * 100;
if (this.percentageCom === 100) {
// this.isHidden = true;
this.$timer.unsubscribe();
}
});
Also, this will probably process so fast, you won't see it.