not updated template view -> it needs mous scroll, or clear input - angularjs

I'm using angular.js with stomp-websockets,sock.js under by this tutorial http://g00glen00b.be/spring-websockets-angular/. I'm getting messages from websocket, but template view isn't refreshed after message from websocket is arrived. When I want to update template view I need to clear input box, or scroll with mouse.
$scope.initiator = false;
$scope.socket = {
client: null,
stomp: null
};
$scope.initSockets = function() {
$scope.socket.client = new SockJS('/myUrl');
$scope.socket.stomp = Stomp.over($scope.socket.client);
$scope.notifyMessage = function(message) {
$scope.initiator = true;
$scope.messages.push(message)
};
$scope.socket.stomp.connect({}, function() {
$scope.socket.stomp.subscribe("/topic/messages", $scope.notifyMessage);
});
$scope.socket.client.onclose = $scope.reconnect;
};
$scope.initSockets();
template view:
<ul><li ng-repeat="item in messages track by $index">{{item}}</li></ul>

You probably need to use scope.$apply to have it take effect. afaics sock.js is not made for angular, so you need to let angular know something has changed on the scope. You do this using scope.$apply.
$scope.notifyMessage = function(message) {
$scope.$apply(function(){
$scope.initiator = true;
$scope.messages.push(message)
});
};

Related

Implementing notification alerts in angularjs

I was wondering how an error alert would be implemented using angularjs.
Required functionality:
An alertQueue consists of all the alerts to be displayed to the user. These alerts are deleted from the queue after a span of 3 seconds. The user himself can close the alert by clicking the close button.
This AlertService must be the core service. Alerts are rendered in the view as <alert-list></alert-list>i.e using a component alertList.
Should be able to update alerts from other controllers like: AlertService.alert("my alert").
so far what I have done?
angular.
module('core').
factory('AlertService', [function() {
var alertQueue = [];
var addAlert = function(message, type){
message = {message: message, type: type};
alertQueue.push(message)
};
var deleteAlert = function(alert){
alertQueue.splice(alertQueue.indexOf(alert), 1);
};
return{
warning: function(msg){
addAlert(msg, "warning");
},
success: function(msg){
addAlert(msg, "success");
},
removeAlert: function(alert){
deleteAlert(alert);
},
getAlerts: function(){
return alertQueue;
}
}
}]);
angular.
module('alertApp').
component('alertList', {
templateUrl: '/static/js/app/aurora-alert/aurora-alert.template.html',
controller: ['$routeParams','$scope', 'Aurora',
function AlertController($routeParams, $scope, AlertService) {
var self = this;
self.alertQueue = AlertService.alertQueue;
self.alert = function(){
var message = arguments[0];
AlertService.warning(message);
};
self.removeAlert = function(alert) {
AlertService.removeAlert(alert);
};
}
]
});
I know that I'm doing something wrong in the above code and in its logic. I said above that I require the <alert-list></alert-list> component. So the alertService is injected as a dependency into alertController. But how am I going to raise the alert from other controllers? I know we can use $scope.$broadcast but that doesn't feel right.
Please explain how to achieve this? No third party libraries are to be used.
I think you are going about it only slightly incorrectly. Your alert-list should be responsible only for displaying and removing alerts, not for creating them. Leave the creation of alerts to your controllers
So for example, if you run into an error with an ApiSerivce:
DemoCtrl(AlertService, ApiService) {
ApiService.submitForm({some:data}).then(function() {
//something successfull happened
}).catch(function(error) {
AlertService.warning("Something bad happened calling the API serivce");
});
}
Then you can change your AlertService to broadcast an event when a new alert is created that the alert-list can listen to:
factory('AlertService', ["$rootScope", function($rootScope) {
var alertQueue = [];
var addAlert = function(message, type){
message = {message: message, type: type};
alertQueue.push(message)
$rootScope.$broadcast("new-alert"); //notify the list that there are new alerts
};
This is how you would listen to it in your alert-list:
$scope.$on("new-alert", function() {
self.alertQueue = AlertService.alertQueue;
});
This way, as soon as an alert is created, the alert-list is instantly updated with the latest queue of alerts.
You would probably want to do the same thing for alert deletion.

Hiding page loader only after completion of data load in the page view

Currently am working with angularjs.
now i have an ng-click function,it is called when checkbox is checked or unchecked. i have a full page loader and when i click on the check box it will be displayed. It will be hidden only after loading the complete view. I tried this code and it is not working
here comes the click
<input type="checkbox"id="myId" ng-click="sampleClick(1)">
here is the js code
$scope.sampleClick = function(type) {
var param = new Object();
param.type = type;
//side bar listing
Data.post('url/testurl', param).then(function(data){
$scope.all = '';
$scope.all = angular.fromJson(data);
console.log("Contents is trying to load.");
$scope.$on('$viewContentLoaded',function(event, viewConfig){
console.log("Contents did load.");
$rootScope.setVariable = 1;
});
}, function (error) {
$rootScope.setVariable = 1;
});
}
I want to set the $rootScope.setVariable = 1 only when the view section is completely loaded.(and each time when I click on the check box)
Note : when I use $viewContentLoaded at the time of page load, it works without any issue. But when I use the same along with the ng-click function, i don't get any response.
If the view is rendered using ng-repeat, why can't you use ng-repeat directive to handle the end of render and then initiate a function call.
for example
<div ng-repeat="i in things" repeat-done>
// here you use i to render your filter with checkbox
</div>
and directive will look like
app.directive('repeatDone', function($rootScope) {
return function(scope, element, attrs) {
if (scope.$last){
//set your variables or do you job
}
};
})
You can set one flag you can set it value true/false.
for example(I am taking your example so you can understand it batter):-
$scope.loading = false;
$scope.sampleClick = function(type) {
var param = new Object();
param.type = type;
//side bar listing
Data.post('url/testurl', param).then(function(data){
$scope.all = '';
$scope.all = angular.fromJson(data);
console.log("Contents is trying to load.");
$scope.loading = true;
$scope.$on('$viewContentLoaded',function(event, viewConfig){
$scope.loading = false;
console.log("Contents did load.");
$rootScope.setVariable = 1;
});
}, function (error) {
$rootScope.setVariable = 1;
});
}
here i have take $scope.loading variable which default value is false, when you click on checkbox then it will be set to true so at that time you can show the loader and when it get the data it will be set to false so it will stops loading.
I hope you understand.

ng-include template variable not changing on $scope.$watch (no button trigger)?

I have a controller 'AController' that has a template variable, $scope.tpl.partialtemplate1 = 'initialcontactlist.html'.
'AController' basically is in charge of an entire page, 'mainpage.html', where we have
<div ng-include="tpl.partialtemplate1"></div>
On 'mainpage.html', there is a form to add contacts. This form is not part of 'partialtemplate1''s views.
Upon submitting the form, I want the HTML view for 'partialtemplate1' to be reset to what it was on initial page load, because it will then reload the latest list of contacts.
I have tried things like incrementing a variable after each new contact is successfully added, and then having that variable watched and the partialtemplate variable changed.
for example, in 'AController':
$scope.tpl = {};
$scope.contactcount = 0;
$scope.contactsignupdata = new Contact();
$scope.tpl.partialtemplate1 = 'initialcontactlist.html';
$scope.successmessage = null;
$scope.addcontact = function() {
$scope.contactsignupdata.$save();
$scope.successmessage = 'Saved!';
$scope.contactsignupdata = new Contact();
$scope.contactcount = $scope.contactcount + 1;
};
$scope.$watch('contactcount', function(newValue, oldValue) {
$scope.$apply(function() {
$scope.tpl.partialtemplate1 = null;
$scope.tpl.partialtemplate1 = 'initialcontactlist.html';
});
/*$scope.partialtemplate1 = 'projecttasklists.html';*/
});
Why isn't the partialtemplate variable getting changed? Yes, the contact gets successfully saved each time - I took care of that with the Rails factory...
Your code sets partialtemplate1 to null, then straight back to 'initialcontactlist.html'. As far as Angular is concerned, nothing is changed. True bindings are not supported meaning that just because you changed partialtemplate1, doesn't mean it immediately happens or triggers any special events. For this specific scenario, you would have to set partialtemplate1 to null, set a timer, then trigger the change back to 'initialcontactlist.html'
I do not recommend this by the way
$scope.$watch('contactcount', function(newValue, oldValue) {
$scope.$apply(function() {
$scope.tpl.partialtemplate1 = null;
$timeout(function() {
$scope.tpl.partialtemplate1 = 'initialcontactlist.html';
}, 1000);
});
});
I highly recommend
Creating an API for Contacts that you can query. That way when a Contact is created, updated, or removed you can handle it yourself in a couple ways:
You can requery the data source each time something changes
If the API returns data related to the change, you don't need to requery
You should look into creating Angular Services and/or Factories to handle this. In fact it is quite easy to implement if it is a true REST API using $resource. If it is not a RESTful resource, you can use $http for custom queries
I solved this problem with $emit.
In the HTML file, upon pressing the submit button for the "Add a contact" form, two events are triggered (separated by the apostrophe button).
ng-click="addcontact(contactsignupdata.name);$emit('MyEvent')"
</form>
{{successmessage}}
In the controller file:
$scope.successmessage = null;
$scope.tpl = {};
$scope.tpl.partialtemplate1 = 'initialcontactlist.html';
$scope.addcontact = function(value) {
$scope.contactsignupdata.$save();
$scope.successmessage = 'Saved ' + $scope.contactsignupdata.name;
$scope.contactsignupdata = new Contact();
};
$scope.$on('MyEvent', function() {
$scope.tpl.partialtemplate1 = null;
$scope.resettofullcontactslist($scope.tpl.partialtemplate1);
});
$scope.resettofullcontactslist = function(value) {
$scope.tpl.partialtemplate1 = 'initialcontactlist.html';
};

UI Notifications with angular js

I have to implement some standard notification UI with angular js. My approach is the following (simplified):
<div ng-controller="MainCtrl">
<div>{{message}}</div>
<div ng-controller="PageCtrl">
<div ng-click="showMessage()"></div>
</div>
</div>
And with the page controller being:
module.controller("PageCtrl", function($scope){
counter = 1
$scope.showMessage = function(){
$scope.$parent.message = "new message #" + counter++;
};
});
This works fine. But I really don't like the fact that I need to call $scope.$parent.
Because if I am in another nested controller, I will have $scope.$parent.$parent, and this becomes quickly a nightmare to understand.
Is there another way to create this kind of global UI notification with angular?
Use events to send messages from one component to another. That way the components don't need to be related at all.
Send an event from one component:
app.controller('DivCtrl', function($scope, $rootScope) {
$scope.doSend = function(){
$rootScope.$broadcast('divButton:clicked', 'hello world via event');
}
});
and create a listener anywhere you like, e.g. in another component:
app.controller('MainCtrl', function($scope, $rootScope) {
$scope.$on('divButton:clicked', function(event, message){
alert(message);
})
});
I've created a working example for you at http://plnkr.co/edit/ywnwWXQtkKOCYNeMf0FJ?p=preview
You can also check the AngularJS docs about scopes to read more about the actual syntax.
This provides you with a clean and fast solution in just a few lines of code.
Regards,
Jurgen
You should check this:
An AngularJS component for easily creating notifications. Can also use HTML5 notifications.
https://github.com/phxdatasec/angular-notifications
After looking at this: What's the correct way to communicate between controllers in AngularJS? and then that: https://gist.github.com/floatingmonkey/3384419
I decided to use pubsub, here is my implementation:
Coffeescript:
module.factory "PubSub", ->
cache = {}
subscribe = (topic, callback) ->
cache[topic] = [] unless cache[topic]
cache[topic].push callback
[ topic, callback ]
unsubscribe = (topic, callback) ->
if cache[topic]
callbackCount = cache[topic].length
while callbackCount--
if cache[topic][callbackCount] is callback
cache[topic].splice callbackCount, 1
null
publish = (topic) ->
event = cache[topic]
if event and event.length>0
callbackCount = event.length
while callbackCount--
if event[callbackCount]
res = event[callbackCount].apply {}, Array.prototype.slice.call(arguments, 1)
# some pubsub enhancement: we can get notified when everything
# has been published by registering to topic+"_done"
publish topic+"_done"
res
subscribe: subscribe
unsubscribe: unsubscribe
publish: publish
Javascript:
module.factory("PubSub", function() {
var cache, publish, subscribe, unsubscribe;
cache = {};
subscribe = function(topic, callback) {
if (!cache[topic]) {
cache[topic] = [];
}
cache[topic].push(callback);
return [topic, callback];
};
unsubscribe = function(topic, callback) {
var callbackCount;
if (cache[topic]) {
callbackCount = cache[topic].length;
while (callbackCount--) {
if (cache[topic][callbackCount] === callback) {
cache[topic].splice(callbackCount, 1);
}
}
}
return null;
};
publish = function(topic) {
var callbackCount, event, res;
event = cache[topic];
if (event && event.length > 0) {
callbackCount = event.length;
while (callbackCount--) {
if (event[callbackCount]) {
res = event[callbackCount].apply({}, Array.prototype.slice.call(arguments, 1));
}
}
publish(topic + "_done");
return res;
}
};
return {
subscribe: subscribe,
unsubscribe: unsubscribe,
publish: publish
};
});
My suggestion is don't create a one on your own. Use existing models like toastr or something like below.
http://beletsky.net/ng-notifications-bar/
As suggested above, try to use external notifications library. There're a big variety of them:
http://alertifyjs.com/
https://notifyjs.com/
https://www.npmjs.com/package/awesome-notifications
http://codeseven.github.io/toastr/demo.html

Execute a function after two or more events are fired in Backbone

I have two buttons:
<button id="1">button 1</button>
<br>
<button id="2">button 2</button>
I want to make an alert message to appear only after the two buttons are clicked, this is part of a larger app for which I am using Backbone.js, so far I have this:
var test = {};
_.extend(test, Backbone.Events); //in order to use the backbone events feature
test.on("hello bye", function(){
alert("works!");
});
$("#1").click(function() {
test.trigger("hello");
});
$("#2").click(function() {
test.trigger("bye");
}?);
This presents the message whenever one of the two buttons are clicked, but I need it to work only after the two buttons are clicked. Is there a way to achieve this using the Backbone.Events feature or perhaps using jQuery?
Fiddle: http://jsfiddle.net/ccgRN/
Thanks
Here's an idea using promises: http://jsfiddle.net/LFJGL/1/
Edit: fixed for older browsers:
var test = {};
var promises = [];
_.extend(test, Backbone.Events); //in order to use the backbone events feature
function GetAPromise() {
var p = $.Deferred();
promises.push(p);
return p;
}
var events = "hello bye".split(" ");
_.each(events,function(event) {
var p = GetAPromise();
test.on(event, function() {
p.resolveWith(event);
});
});
$("#1").click(function() {
test.trigger("hello");
});
$("#2").click(function() {
test.trigger("bye");
});
$.when.apply($, promises).done(function() {
alert('triggered');
//might be nice to remove the event triggers here.
});​
A minimalist implementation would be to create a couple variables to keep track of what events have been fired.
var helloClicked = false;
var byeClicked = false;
var test = {};
_.extend(test, Backbone.Events);
test.on('hello bye'), function() {
if (helloClicked && byeClicked) {
alert('works!');
}
});
$('#1').click(function() {
helloClicked = true;
test.trigger('hello');
});
$('#2').click(function() {
byeClicked = true;
test.trigger('bye');
});

Resources