I am facing a rather strange issue with AngularJs.
I have an AngularJS (AJS henceforth) application which is listening to certain SignalR hubs.
When a SignalR hub receives a message (and the data object is passed in that call too), it fires an event in AJS which in turn shows a Toastr on the UI.
Now I have around 5 different types of SignalR hubs that initiate different Toasts on the UI.
I added another Toast (as per the new requirement) which is initiated by yet another SignalR hub.
Problem:
This new toast that I have implemented does show up when the SignalR hub receives a message, but the data is not rendered. What I mean is that the toast shows up with {{ }} these brackets.
Other toasts are following the same routine and are showing up just fine, except for the new one.
The strangest part is that I have another AJS method running at an interval of around 30 seconds that fetches some count values from the database (using WEB API in the background). And when this action completes the {{ }} disappear and the values show up just fine (I keep the toastr alive for that while by keeping my mouse cursor over it, or else it will disappear).
//SignalR hub initiates the following method
$scope.$on("on_remote_event", function (argEvent, argData) {
var toastrHtml = "<div class='my-toast-style'><div class='myLabel'>Client : {{ argData.ClientName }}</div><div class='myLabel'>Order Date: {{ argData.OrderedOn | date:'MM/dd/yyyy hh:mm a' }} EST</div></div>";
generateAlertToast(toastrHtml, "Order Info", argData);
});
//this method will generate the toast as per the information passed to it
function generateAlertToast(toastrHtml, toastrTitle, argData) {
toastr.options = {
"closeButton": true,
"newestOnTop": true,
"progressBar": true,
"preventDuplicates": true,
"timeOut": 60000,
"positionClass": "toast-bottom-right"
}
var scope = $rootScope.$new();
scope.argData = argData;
//console.info(scope.argData);
var compiledHtml = $compile(toastrHtml)(scope);
toastr.info(compiledHtml, toastrTitle);
};
Now I know that somehow AJS is not evaluating the html for the toastr. But how and where is the problem is what i am banging my head against!!
Please help.
So sorry guys!!
It was my stupid mistake that I made in the SignalR side code due to which the above mentioned code was not working.
Basically when I broadcast the message received from SignalR I forgot to enclose the broadcasting line in the rootscope apply.
$rootScope.$apply(function () {
//broadcast event here
});
And since I did not write the rootscope apply, AJS was not able to run the digest cycle and hence was waiting for me or some other method to initiate the digest cycle and evaluate the values on the UI.
Lesson Learnt.
Thank you :)
Related
I am working on a site that uses the great jsPlumb library to create a node interface.
jsPlumb has an event 'beforeDrop' that is triggered before a connection between two endpoints are connected, that I want to use to check a condition, and then decide to allow the connection or not.
It the connection is not allowed, I want to use ngToast to show a message to the user.
This is my 'beforeDrop' function
jsPlumb.bind('beforeDrop', function(info){
// Check that they property types match
var outNodeType = $('#'+info.sourceId).data( "ptype" );
var inNodeType = $('#'+info.targetId).data( "ptype" );
if(outNodeType !== inNodeType){
showMessage('warning', '<strong>Error:</strong> unable to connect '+outNodeType+' to '+inNodeType)
return false // false for not establishing new connection
}
return true; // true for establishing new connection
});
And this is the function that shows the ngToast message:
function showMessage(messageType, message){
ngToast.warning({
class: messageType,
content: message
});
}
The problem is that the ngToast message does not appear until I click anywhere on the page. Once I click, the message appears and everything works.
I don't know if this is an issue with jsPlumb and angularjs, or a problem with how I am calling the ngToast function.
I would really appreciate any suggestions as to how to resolve this. TIA!
jsPlumb event will be conaidered as event outside of angular context. Seems like you are calling angular code from outside of angular code. For make sync angular, you need to call $scope.apply() after calling the toaster message method. So that toast will get shown as soon as you clicked on it.
I m actually facing a problem while developping my chat application between angularjs (BTFord library) and NodeJS Socket.IO...
In fact, I need a user A to send a message to a user B.
- If I log the message in node, it works perfectly, and it's sent only one time.
- In angular js the event on("message") is called 3 or 4 times, and I had the message to send 3 or 4 times
It's a problem for me ... I need to receive the message only one time.
Is there any problem with this ?
Thanks for advance
I had a similar situation, it ends up that the controller was executing more than once, so be aware of that, check if it's running more than once using a message with console.log, beign said that probably you are registering several listeners on the client side to avoid that put a condition in your client: (let's asume that you are registering the events on $rootscope)
if( $rootScope.$$listeners['socket:someListener']===null || $rootScope.$$listeners['socket:someListener']===undefined){
$rootScope.$on('socket:someListener', function(ev, data){
//Client code
}
}
that will register your listener only once!
cheers!
Events handlers are not destroyed when switching/reloading controllers. So you may have subscribed multiples times to your event.
See the docs : https://github.com/btford/angular-socket-io#socketremovelistener
Can you try this :
angular.module('Foo').controller('BarCtrl', function ($scope, socket) {
socket.on('message', function (msg) {
// ... do something
});
$scope.$on('$destroy', function() {
socket.removeListener('message');
});
});
I have a web worker whose job it is to periodically poll a webservice for data, and then insert that data into an IndexedDB database.
Is there any recommended way of notifying an AngularJS module of updates to the object stores, since all the changes happen outside of Angular?
Call $scope.$apply() when your web worker finishes. You will want to grab the "root" element of where you bootstrapped your ng-app. For example:
HTML:
<div id="rootElement" ng-app="myApp">
...
</div>
JavaScript:
//Call $apply() to start an angular digest cycle where any updates you made to objects
//angular knows about will update the view.
angular.element(document.getElementById("rootElement")).scope().$apply();
Edit: Including sample onmessage subscribe.
Since your worker is only writing to the IndexedDb, you will need to also use postMessage to get that data back to your HTML (and angular). For example, in your HTML:
var worker = new Worker("pathToWorker.js");
//Start worker in some way
worker.postMessage();
//Use data from the worker to update scope.
worker.addEventListener("message", function (e) {
//Get the data from the worker
var indexData = e.data;
//Update a scope variable (scopevar)
//Note: The element you query with angular.element must have the scope variable you
//are interested in.
angular.element(document.getElementById("MainController")).scope().$apply('scopevar=' + JSON.stringify(indexData));
}, false);
From your worker:
self.addEventListener("message", function (e) {
//Do work to write to IndexedDb
//PostMessage using the indexDb Data so angular can know about it.
self.postMessage({title: "Foo", data: "Bar"});
}
Without seeing your code, I can't give a more specific example, but postMessage from the worker is definitely the way to get data from your worker back to angular.
I'm using parse with angularjs to authenticate users. Here is the login function.
$scope.doLogin = ->
Parse.User.logIn $scope.currentUser.username, $scope.currentUser.password,
success: (user) ->
console.log user
$scope.currentUser = user
error: (user, error) ->
console.log error
And here is the form (used twice in same page, navbar dropdown and in page content):
%form{"ng-submit" => "doLogin()"}
%input{"ng-model" => "currentUser.username", type: "text"}
%input{"ng-model" => "currentUser.password", type: "password"}
%button.btn.btn-block
%center Connexion
The problem is that whenever the form is submitted, I can see the user object in console, but $scope.currentUser doesn't always get updated. Sometimes I have to submit the form 3 or 4 times in a row for it to get updated.
What am I doing wrong ? Thank you.
perhaps you should include $scope.apply() in your success callback. From the Angularjs docs:
"$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life-cycle of exception handling, executing watches."
I had a similar problem with Parse and I'd solved it with $scope.apply().
Here is my controller
ps. note that I'm relatively new to angularjs and the above code may not be the most efficient :)
What is the best way to pass message in the below scenario.
In the success scenario of $scope.p.$save, the result contains a message (res.message), which I like to display in the next view ($location.path("/test/"+res.reply.Id)). Without AngularJS, I may pass it in the url or save it in session cookies. But, I guess there might be a better way in AngularJS as there is no browser redirect and the state should be available. What is the best way to achieve this?
Setting it in rootScope shows it while I use browser back button, and the scope of the message should only for the first navigation to the new view.
function NewCtrl(Phone, $location, $rootScope, $scope) {
$scope.p = new Phone();
$scope.save = function () {
$scope.p.$save(
{},
function (res) {
$rootScope.message = res.message **//<-- this will cause message still set when using browser back button, etc**
$location.path("/test/"+res.reply.Id); **//<-- Req: needs to pass the message to next view**
}, function (res) {
//TODO
}
);
};
}
....
PhoneApp.factory('Phone', function ($resource) {
return $resource('/api/test/:_id')
});
You could use a service which displays the flash on $routeChangeSuccess.
Each time you set a flash message, add it to a queue, and when the route changes take the first item off the queue and set it to the current message.
Here's a demo:
http://plnkr.co/edit/3n8m1X?p=preview
I was looking to implement similar functionality, but actually wanted more of a growl style message.
I've updated the excellent plinkr code that Andy provided above to include a 'pop' method that leverages the toastr growl-style notification library.
My update also lets you to specify the notification type (info, warning, success, error) and title.
The 'pop' method skips adding the message to the queue, and instead pops it up on the screen immediately. The set/get functionality from Andy's previous plinkr remains mostly unchanged.
You can find my update here: http://plnkr.co/edit/MY2SXG?p=preview
I don't believe there's a way to do this default to AngularJS. Your best bet would just be passing the message (encoded) through a query string.