Frequent updates to Angular controller not reflected in view - javascript

Working on an interesting issue with Angular and frequent updates from SignalR.
on the server, I have a singleton instance of a manager which is currently set to send updates every 500ms
public void StartTest() {
var i = 0;
while (i < 1000) {
if (_cancelRequested) {
_cancelRequested = false;
return;
}
_context.Clients.All.sent(i++);
Thread.Sleep(500);
in my view i'm using "controller as vm" syntax:
{{vm.status}}
finally in my controller (on signalR initialization):
// Using 'Controller As' syntax, so we assign this to the vm variable (for viewmodel).
var vm = this;
vm.status = '';
...
function activateSignal() {
var hub = $.connection.mailerHub;
hub.client.sent = function (counter) {
log(' singal_R ## event: sent ' + counter); // <-- this works on every update
vm.status = counter; // this works only on last update, or if stop is pressed it writes the most recent update.
};
that code does 2 things: writes to log (this sends a toastr update to the browser), and sets the status variable on the controller.
the log comments are reflected in the browser as expected, every half second i get the nice toastr notification with event number, however setting the variable doesn't have any effect, until i request stop, or it reaches 999.
when i press stop, vm.status gets written out with last sequence number, or when the entire thing stops, it writes out 999.
so it appears to be some issue with frequency of setting properties on vm? would i need something like $scope.$watch to make this work?

Well, it is not clear enough for me exactly what you are trying to achieve, but it looks like you are updating your model outside the control of angular. So, you need to notify angular of what you are doing.
Most likely, something like this should solve the problem:
hub.client.sent = function (counter) {
$scope.apply(function(){
log(' singal_R ## event: sent ' + counter);
vm.status = counter;
});
};

Related

Angular 2, throttleTime on observable from subject

Im trying to make throttleTime take effect, but for some reason it does not kick in. I have the following:
// Class Properties
private calendarPeriodSubject: Subject<x> = new Subject<x>();
private calendarPeriodObservable$ = this.calendarPeriodSubject.asObservable();
// Throttling fails here (Inside constructor):
const calendarPeriodSubscription = this.calendarPeriodObservable$.pipe(throttleTime(750)).subscribe(async (calendar: x) => {
// Do http stuff here
}
});
The subject gets called like this:
this.calendarPeriodSubject.next(x);
I also tried with:
this.calendarPeriodSubject.pipe(throttleTime(1000)).subscribe({next: (x) => x});
I would like to process the FIRST time, and the following clicks should not have any effect before after ieg 750ms - To prevent the server from getting spammed basically.
Anyone has any idea?
Thanks!
The problem is that you are using the wrong operator for your use case. The way I understand your explanation you want to send through your first call and stop any further calls to your Server for some amount of ms. But what throttleTime(sec) does is simply put a timer on the action and execute it sec ms later. So you server will still be spammed, just a few ms later.
Your case screams debounceTime() for me. debounceTime docu
This disables any further data to be passed though the Observable for the specified time after a value has been emitted.
Therefore your code should be fine if you use something like:
const calendarPeriodSubscription =
this.calendarPeriodObservable$.pipe(debounceTime(750)).subscribe((calendar: x) => {
// Stuff with returned data
});

Using WebSpeech API through AngularJS

I have been trying to use the WebSpeech Api through angularjs. Everything seems to work but the model doesn't get updated at once.
If I start the recognition again, the model updates. Seems like some inner loop/other construct is holding angular to see the changes.
Here is the codepen that I made.
Steps to reproduce:
1. Click start, and speak
2. After recognition detects end of speech hit start once again to start another recognition.
3. As soon as the second recognition is started, the model is updated with previous transcript.
Note: If a do console.log as below then it shows correct transcript, means the recognition part is working fine.
if(event.results[i].isFinal) {
self.final = self.final.concat(event.results[i][0].transcript);
console.log(event.results[i][0].transcript);
}
Everything seems perfect, except you forgot to call $scope.$apply(); when you modified values to get it effect on view. So it should be like this,
angular.module('speech',[]);
angular.module('speech').controller('speechController', function($scope) {
this.rec = new webkitSpeechRecognition();
this.interim = [];
this.final = '';
var self = this;
this.rec.continuous = false;
this.rec.lang = 'en-US';
this.rec.interimResults = true;
this.rec.onerror = function(event) {
console.log('error!');
};
this.start = function() {
self.rec.start();
};
this.rec.onresult = function(event) {
for(var i = event.resultIndex; i < event.results.length; i++) {
if(event.results[i].isFinal) {
self.final = self.final.concat(event.results[i][0].transcript);
console.log(event.results[i][0].transcript);
$scope.$apply();
} else {
self.interim.push(event.results[i][0].transcript);
}
}
};
});
I have updated your codepen with working solution.
AngularJs creates a "watch" internally for the all data-bindings created in view and call $scope.$digest() which inturns iterate thorugh all watches and checks if any of the watched variables have changed. When you call $scope.$apply() it internally calls $scope.$digest() so data-binding gets refreshed.
Listener directives, such as ng-click, register a listener with the DOM. When the DOM listener fires, the directive executes the associated expression and updates the view using the $apply() method.
When an external event (such as a user action, timer or XHR) is received, the associated expression must be applied to the scope through the $apply() method so that all listeners are updated correctly (ref).
So in your case view gets update when you click next start button again (ng-click) and not when recording event occurs.
Also would be usefult to read this

Javascript / meteor android return objects to templates

I have the following piece of code in my meteor app
if (Meteor.isClient) {
Meteor.startup(function(){
var onSuccess = function(acceleration){
alert( acceleration);
};
var onError = function(acceleration){
return "error";
}
var options = { frequency: 3000 }; // Update every 3 seconds
var getAcc = navigator.accelerometer.watchAcceleration(onSuccess, onError, options);
});
}
What that does is retrieve the android phones accelerometer data every 3 seconds, and on a successful poll it will show the object in an alert. This works fine.
However, I don't want this polling code to be in the startup function. I want to have more control over when this is executed
I have a template where I want to display the accelerometer values. I changed the onSuccess method in the code above to return the object instead of alerting (the rest of the startup code is the same):
var onSuccess = function(acceleration){
return acceleration;
};
My template looks like this:
Template.rawData.helpers({
acc: function(){
alert(getAcc);
return getAcc;
}
});
What I'm expecting to happen is for the accelerometer data to be stored in getAcc in the startup function, but then to return it through acc to my webpage. This does not seem to happen. The alert in the template doesn't occur either
Is there a way to access these cordova plugins from outside of the startup function? Am I just incorrectly returning the objects between the startup and template sections?
I guess my other overarching question is this: I'm not sure how to display those accelerometer values through a template if theyre gathered in the startup function, and not from a template helper
You need to have the template update reactively when the data changes. To do that set up a reactive variable that gets updated by the callback. First, install the package:
meteor add reactive-var
Then, when the template is created, create the reactive variable and start watching the callbacks:
Template.rawData.onCreated(function() {
self = this;
self.rawValue = new ReactiveVar();
self.accWatchID = navigator.accelerometer.watchAcceleration(
function(acc) { self.rawValue.set(acc); }, function() {}, { frequency: 3000 }
);
});
Your template helper can then return the value of the reactive variable, and your template will be updated whenever it changes:
Template.rawData.helpers({
acc: function() {
return Template.instance().rawValue.get();
}
});
(Note that in your original code, since the alert wasn't being called, there must be a problem in your template. Does it have the right name?)
Finally, you should stop the callback when the template is destroyed:
Template.rawData.onDestroyed(function() {
navigator.accelerometer.clearWatch(this.accWatchID);
});
Note that I've just typed that code here without testing it. You may need to fine tune it a little if it doesn't work straight away.

How to throttle "actions" (updates) to take on a changed source on a page

I searched a lot for a solution to this certainly-not-unique problem, but I have not found anything that will work in my context of an HTML page.
I have an input text that contains some kind of source-code that generates something, and I can show a preview of that something on the same HTML page (by updating the background image, for example). Note that the source could be a LaTeX file, an email, a Java program, a ray-trace code, etc. The "action" to generate the preview has a certain cost to it, so I don't want to generate this preview at each modification to the source. But I'd like the preview to auto-update (the action to fire) without the user having to explicitly request it.
Another way to phrase the problem is to keep a source and sink synchronized with a certain reasonable frequency.
Here's my solution that's too greedy (updates at every change):
$('#source-text').keyup(function(){
updatePreview(); // update on a change
});
I tried throttling this by using a timestamp:
$('#source-text').keyup(function(){
if (nextTime "before" Now) { // pseudocode
updatePreview(); // update on a change
} else {
nextTime = Now + some delay // pseudocode
}
});
It's better, but it can miss the last updates once a user stops typing in the source-text field.
I thought of a "polling loop" for updates that runs at some reasonable interval and looks for changes or a flag meaning an update is needed. But I wasn't sure if that's a good model for an HTML page (or even how to do it in javascript).
Use setTimeout, but store the reference so you can prevent it from executing if further editing has occurred. Basically, only update the preview once 5 seconds past the last keystroke has passed (at least in the below example).
// maintain out of the scope of the event
var to;
$('#source-text').on('keyup',function(){
// if it exists, clear it and prevent it from occuring
if (to) clearTimeout(to);
// reassign it a new timeout that will expire (assuming user doesn't
// type before it's timed out)
to = setTimeout(function(){
updatePreview();
}, 5e3 /* 5 seconds or whatever */);
});
References:
clearTimeout
setTimeout
And not to self-bump, but here's another [related] answer: How to trigger an event in input text after I stop typing/writing?
I tweaked #bradchristie's answer, which wasn't quite the behavior I wanted (only one update occurs after the user stops typing - I want them to occur during typing, but at a throttled rate).
Here's the solution (demo at http://jsfiddle.net/p4u2mhb9/3/):
// maintain out of the scope of the event
var to;
var updateCount = 0;
var timerInProgress = false;
$('#source-text').on('keyup', function () {
// reassign a new timeout that will expire
if (!timerInProgress) {
timerInProgress = true;
to = setTimeout(function () {
timerInProgress = false;
updatePreview();
}, 1e3 /* 1 second */ );
}
});

node.js setInterval not working in custom module

I am developing a web application in node.js to collect data from devices on a network using snmp. This is my first real encounter with node.js and javascript. In the app each device will be manipulated through a module I named SnmpMonitor.js. This module will maintain basic device data as well as the snmp and database connection.
One of the features of the app is the ability to constantly monitor data from smart metering devices. To do this I created the following code to start and stop the monitoring of the device. It uses setInterval to constantly send a snmp get request to the device. Then the event listener picks it up and will add the collected data to a database. Right now the listener just prints to show it was successful.
var dataOIDs = ["1.3.6.1.2.1.1.1.0","1.3.6.1.2.1.1.2.0"];
var intervalDuration = 500;
var monitorIntervalID;
var dataCollectionEvent = "dataCollectionComplete";
var emitter = events.EventEmitter(); // Uses native Event Module
//...
function startMonitor(){
if(monitorIntervalID !== undefined){
console.log("Device monitor has already started");
} else {
monitorIntervalID = setInterval(getSnmp,intervalDuration,dataOIDs,dataCollectionEvent);
emitter.on(dataCollectionEvent,dataCallback);
}
}
function dataCallback(recievedData){
// receivedData is returned from getSnmp completion event
// TODO put data in database
console.log("Event happened");
}
function stopMonitor(){
if(monitorIntervalID !== undefined){
clearInterval(monitorIntervalID);
emitter.removeListener(dataCollectionEvent,dataCallback);
} else {
console.log("Must start collecting data before it can be stopped");
}
}
//...
I also have a test file, test.js, that requires the module, starts monitoring, waits 10 seconds, then stops it.
var test = require("./SnmpMonitor");
test.startMonitor();
setTimeout(test.stopMonitor,10000);
My problem is that the setInterval function in startMonitor() is not being run. I have tried placing console.log("test"); before, inside, and after it to test it. The inside test output never executes. The monitorIntervalID variable is also returned as undefined. I have tested setInterval(function(){ console.log("test"); },500); in my test.js file and it runs fine with no issues. I feel like this is a noobie mistake but I just can't seem to figure out why it won't execute.
Here is a link to the entire module: SnmpMonitor.js
I not sure exactly what was wrong but I got it to work by overhauling the whole class/module. I thought the way I had it was going to allow me to create new monitors objects but I was wrong. Instead I created two functions inside the monitor file that do the same thing. I changed the start function to the following.
SnmpMonitor.prototype.start = function() {
var snmpSession = new SNMP(this.deviceInfo.ipaddress,this.emitter);
var oids = this.deviceInfo.oids;
var emit = this.emitter;
var duration = this.intervalDuration;
this.intervalID = setInterval(function(){
snmpSession.get(dataCollectionEvent,emit,oids);
},duration);
};
The setInterval function seems to work best when the callback function is set inside an anonymous function, even though technically you can pass it directly. Using the this. notation I created some class/module/function variables (whatever its called in js) that are in scope of the whole class. For some reason the variables accessed through this. do not work so well when directly in a function or expression so I created temp variables for them. In my other version all the variables were global and js doesn't seem to like that.

Categories

Resources