Multiple angularjs controllers using ngstomp on the same page - angularjs

I am using ngStomp to wire websocket subscriptions to angularjs controllers. When I have one controller on a page by itself it works fine. When I add another controller in an earlier div it stops working.
Is this because it's trying to connect stomp twice to the same endpoint?
My controller declarations are:
var market = 'all';
var orderApp = angular.module('openOrderApp', ['ngStomp']);
orderApp.controller('openOrderController', function ($stomp, $scope) {
$stomp.connect('https://localhost/marketdata-websocket', {})
.then(function (frame) {
console.log('Subscribing to /topic/openOrders');
var subscription = $stomp.subscribe('/topic/openOrders',
function (payload, headers, res) {
$scope.exchanges = payload;
$scope.$apply($scope.orders);
});
$stomp.send("/app/openOrders", market);
});
});
and
var market = 'all';
var statusApp = angular.module('statusApp', ['ngStomp']);
statusApp.controller('statusController', function ($stomp, $scope) {
$stomp.connect('https://localhost/marketdata-websocket', {})
.then(function (frame) {
console.log('Subscribing to /topic/exchangeStatus');
var subscription = $stomp.subscribe('/topic/exchangeStatus',
function (payload, headers, res) {
$scope.exchanges = payload;
$scope.$apply($scope.exchanges);
});
$stomp.send("/app/exchangeStatus", market);
});
});
and in one div there is:
<div class="openOrders row" ng-app="openOrderApp" ng-controller="openOrderController">
followed by:
<div class="exchangeStatus row" ng-app="statusApp" ng-controller="statusController">
When I remove the open orders div the status controller works ok.

After reading the angularjs docs rather than carrying on winging it :P
Cannot have two ng-apps declared on a page. Can obviously have multiple controllers. There's obviously many ways to structure this but I ended up by applying the two controllers to a module.
var app = angular.module('app', ['ngStomp']);
var controllers = {};
controllers.OpenOrderController = function ($stomp, $scope) {
console.log("Creating order controller");
etc...
};
controllers.ExchangeController = function ($stomp, $scope) {
console.log("Creating exchange controller");
etc...
};
app.controller(controllers);
And then in the html tag
<html ng-app>
And the controllers in the divs:
<div ng-controller="OpenOrderController">
<div ng-controller="ExchangeController">

Related

Firebase loading $scope view enter doesn't load

I have a loading problem in Firebase. I want to display a list of images when I open the view but nothing happens till i go back ( there is a flash and i can see my photo list). It's working but not displaying in the opening.
What am i missing please ?
There is the beginning of my Controller view:
'Use Strict';
angular.module('App').controller('valider_photosController', function($scope, $state, $localStorage, Popup, Firebase, $firebaseObject, $ionicHistory, $ionicPopup, $ionicModal, $cordovaCamera) {
$scope.imagestab = [];
var ref_logements = firebase.database().ref('logements');
var ref_images = firebase.database().ref('images');
ref_logements.child(id_logement).child('images').on('child_added', added);
function added(idxSnap, prevId){
ref_images.child(idxSnap.key).once('value', function(datasnap){
var bidule = datasnap.val();
bidule['key'] = datasnap.key;
$scope.imagestab.push(bidule);
console.log('La valeur'+datasnap.key+'donne '+datasnap.val());
});
};
});
Since firebase works with asynchronous calls, by the time firebase responds with your data the angular cycle had already finished and you won't have your scope updated. You can force it by using $scope.$apply();.
ref_images.child(idxSnap.key).once('value', function(datasnap){
var bidule = datasnap.val();
bidule['key'] = datasnap.key;
$scope.imagestab.push(bidule);
$scope.$apply();
});
There is a tool that integrates angular and firebase in a way that you won't have to be concerned with things such as applying the scope. Its called angularfire. I totally recommend you to start using it in your application.
With angularfire you can get your data simply using
$scope.bidule = $firebaseObject(ref_images.child(idxSnap.key));
or
$scope.images = $firebaseArray(firebase.database().ref('images'));
I created a Factory
.factory('Firebase', function ($firebaseArray, $firebaseObject) {
var ref = firebase.database().ref();
return {
all: function (section) {
var data = $firebaseArray(ref.child(section));
return data;
},
getById: function (section, id) {
var data = $firebaseObject(ref.child(section).child(id));
return data;
},
get: function (section, field, value) {
var data = $firebaseArray(ref.child(section).orderByChild(field).equalTo(value));
return data;
}
};
})
And then in my controller, i replaced like you said :
var ref_logements = firebase.database().ref('logements');
var ref_images = firebase.database().ref('images');
ref_logements.child(index2).child('images').on('child_added', added);
function added(idxSnap, prevId) {
var monimage = Firebase.getById('images', idxSnap.key);
$scope.imagestab.push(monimage);
};
And it Works like a charm ! Thank you again :)

Can not figure out how to store $rootScope in angular.bootstrap

I'm trying to call a web service in AngularJS bootstrap method such that when my controller is finally executed, it has the necessary information to bring up the correct page. The problem with the code below is that of course $rootScope is not defined in my $http.post(..).then(...
My response is coming back with the data I want and the MultiHome Controller would work if $rootScope were set at the point. How can I access $rootScope in my angular document ready method or is there a better way to do this?
angular.module('baseApp')
.controller('MultihomeController', MultihomeController);
function MultihomeController($state, $rootScope) {
if ($rootScope.codeCampType === 'svcc') {
$state.transitionTo('svcc.home');
} else if ($rootScope.codeCampType === 'conf') {
$state.transitionTo('conf.home');
} else if ($rootScope.codeCampType === 'angu') {
$state.transitionTo('angu.home');
}
}
MultihomeController.$inject = ['$state', '$rootScope'];
angular.element(document).ready(function () {
var initInjector = angular.injector(["ng"]);
var $http = initInjector.get("$http");
$http.post('/rpc/Account/IsLoggedIn').then(function (response) {
$rootScope.codeCampType = response.data
angular.bootstrap(document, ['baseApp']);
}, function (errorResponse) {
// Handle error case
});
});
$scope (and $rootScope for that matter) is suppose to act as the glue between your controllers and views. I wouldn't use it to store application type information such as user, identity or security. For that I'd use the constant method or a factory (if you need to encapsulate more logic).
Example using constant:
var app = angular.module('myApp',[]);
app.controller('MainCtrl', ['$scope','user',
function ($scope, user) {
$scope.user = user;
}]);
angular.element(document).ready(function () {
var user = {};
user.codeCampType = "svcc";
app.constant('user', user);
angular.bootstrap(document, ['myApp']);
});
Note Because we're bootstrapping the app, you'll need to get rid of the ng-app directive on your view.
Here's a working fiddle
You could set it in a run() block that will get executed during bootstrapping:
baseApp.run(function($rootScope) {
$rootScope.codeCampType = response.data;
});
angular.bootstrap(document, ['baseApp']);
I don't think you can use the injector because the scope isn't created before bootstrapping. A config() block might work as well that would let you inject the data where you needed it.

session username not showing inside angularjs ngView template but does outside of ngView

So I am able to show the username via mongoose passport-local within my successful redirect:
app.get('/profile', isLoggedIn, function (req, res) {
res.render('profile.html', {
user: req.user
});
console.log(req.user.local.username) //correct username for the session
});
...and then use it to display within profile.html:
<p><%= user.local.username %></p>
...yet when trying to display the same within my ng-view:
<div ng-view ng-class="slide" class="slider"></div>
...via my route example.html template:
<p>welcome <%= user.local.username %></p>
..it is displayed as seen: not the correct username...
Is it not possible to include this information within an ngView? Is there a solution to include this info within my template? I have attempted to setup a factory with $http:
angular.module('userService', [])
.factory('Users', function($http) {
return {
get : function() {
return $http.get('/profile');
}
}
});
...but it was returning the entire html, so not a correct solution.
Any guidance is as always welcomed and appreciated, so thanks in advance!
Edit in response:
Getting the value and routing it isnt really the issue. The issue is just getting it to display correctly within the ngView.
heres my updated code:
Rules.get()
.success(function (data) {
$scope.rules = data;
console.log('Rules.get()');
console.log(data);
Users.get()
.success(function (username) {
Users.username = username;
console.log('Users.get()')
console.log(username)
});
});
...and...
angular.module('userService', [])
.factory('Users', function ($http) {
return {
get: function () {
console.log('userService')
return $http.get('/profile/user');
}
}
});
...which returns from:
app.get('/profile/user', function (req, res) {
console.log('profile/user')
console.log(req.user.local.username)
res.json(req.user.local.username);
});
This gives me the same and correct username, but alas what do I call it to get it to display in the ngView? If placed outside of the actual ngView div it displays fine.
From the above {{username}} {{rules.username}} = nothing. What should be the name of the service param within the ngView template (which I am assuming is an entirely new $scope)?
Question is possible duplicate of Angular and Express routing?
You could include ejs templating (like <%= value %>) inside ng-views (see link above), but here are three other options.
Many people pass server data to Angular and let Angular template their views (e.g. in the MEAN stack). This would mean you wouldn't need to have ejs templating in your angular partials. Here are three ways to pass data from your server to Angular (Credit to this blog post: http://mircozeiss.com/how-to-pass-javascript-variables-from-a-server-to-angular/):
1. the MEAN method
The first is what is used in the MEAN stack. You could send all of the data your Angular app needs in the wrapper around your ng-view:
<!-- head -->
<!-- body -->
<!-- ng-view element -->
<script type="text/javascript">window.user = <%- user %>;</script>
<!-- rest of js -->
<!-- /body -->
This way the data can be accessed by your Angular app via a provider like so:
angular.module('myApp')
.factory('UserData', [
function() {
return {
user: window.user
};
}
]);
2. using $http and an api
The second is providing this data via a restful api and requesting the data via a provider using $http or $resource:
angular.module('myApp')
.factory('UserData', [
function($http) {
$http.get('/userAPI').success(function(data){
return data;
});
}
]);
3. Using ng-init
Finally you could use server-side templating inside an ng-init.
<div ng-init="user = <%- data %>"></div>
This would put the data on $scope.user for your Angular app to use.
So here was my solution for anyone else that may be searching:
users.js:
angular.module('userService', [])
.factory('Users', function ($http) {
return {
get: function () {
console.log('userService')
return $http.get('/profile/user');
}
}
});
route.js
$scope.username = {};
.controller('cntrl', function ($scope, Users) {
getCurrentUser(Users, $scope);
})
function getCurrentUser(Users, $scope) {
Users.get()
.success(function (username) {
$scope.username = username.user;
});
}
service.js:
app.get('/profile/user', function (req, res) {
console.log('profile/user')
console.log(req.user.local.username)
res.jsonp({ user:req.user.local.username});
});
then within html (within any ngView template):
<span class="username">{{username}}</span>
the key being:
$scope.username = {};

How would I create a factory that only queries the database once, but can be used by many controllers?

I have a 'messages' factory that will query my database for a list of messages.
I'm using the list of messages in two different places. Once to add a message count indicator, and then once to show a list of messages. Since I'm injecting the service into two different controllers, it seems like it's creating two instances of my factory, and hitting the database twice for the list.
How would I set things up to only ask for the list once, and use the list for both display and count purposes in both controllers?
My factory looks like this:
myApp.factory('messagesService', [
'$rootScope',
function($rootScope) {
var messages = [];
function query() {
// Would actually hit the database asyncronously
messages = ['one', 'two', 'three', 'four'];
console.log('query');
$rootScope.$emit('messages.update');
}
function all() {
return messages;
}
return {
query: query,
all: all
}
}
]);
My controllers are using blocks like this to watch for changes:
$rootScope.$on('messages.update', function() {
$scope.messagesCount = messagesService.all().length;
});
But it means i need a messagesService.query(); in each controller for things to be reliable.
So here are a few jsFiddle examples of it as I have things now:
Doesn't work (only updates the header): http://jsfiddle.net/TSLfc/1/
Works but would break if I didn't load the dashboard controller:
http://jsfiddle.net/TSLfc/2/
Works every time, but queries the server twice:
http://jsfiddle.net/TSLfc/3/
Is there a better way to organize my code? Should I build out the messages factory into it's own full module?
Here (Plunkr) is how I would do it:
I have gone back and modified my previous answer, updating with what we discussed in the comments below as well as using promises instead of the timeout as an asynchronous simulation I was showing before (see revision history for reference).
I also removed every variable/function that didn't need to be returned to the controller from the service object, if it doesn't need to be accessed via the controller than it doesn't need to be included on the returned object.
var myApp = angular.module('myApp', []);
myApp.factory('messagesService', [
'$q',
'$rootScope',
'$http',
function ($q, $rootScope, $http) {
var mService = {};
mService.messages = [];
var queryInit = false;
// We don't need to access this function in the controller
// So I am not going to attach to the returned object
var getMessages = function () {
// Stops each controller from getting messages when loaded
if (!queryInit) {
queryInit = true;
// Using the $q promise library we use 'then()' to handle
// What happens after the async call is returned
// The first function parameter is the success/resolve callback
// The second function parameter is the error/reject callback
mService.query().then(function (successResults) {
// Tell all of the controllers that the data has changed
$rootScope.$broadcast('messages.update');
}, function (errorResults) {
console.error(errorResults);
});
}
};
// Used to force an update from the controller if needed.
mService.query = function () {
var deferred = $q.defer();
$http.get('path/to/file.php')
.success(function (data, status, headers, config) {
// assign the returned values appropriately
mService.messages = data;
// this callback will be called asynchronously
// when the response is available
deferred.resolve(data);
})
.error(function (data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
deferred.reject(data);
});
return deferred.promise;
};
mService.getCount = function () {
return mService.messages.length;
};
mService.all = function () {
return mService.messages;
};
// Initialize the messages
// so we don't need to get the messages in each controller
getMessages();
return mService;
}]);
In your html, on your first controller setup an init function (ng-init="init()") that instantiates the factory:
<div ng-app="myApp">
<div ng-controller="HeaderCtrl" class="header" ng-init="init()">
Messages Count: {{ messageCount }}
</div>
<div ng-controller="DashboardCtrl" class="dashboard">
<ul ng-repeat="message in messages">
<li>{{ message }}</li>
</ul>
<button ng-click="getMessages()">Check for new messages.</button>
</div>
</div>
And in your controllers you just have the $rootScope.$on('messages.update' fn) and you can call manually by calling the services query() function which returns the promise:
myApp.controller('HeaderCtrl', [
'$scope',
'$rootScope',
'messagesService',
function ($scope, $rootScope, messagesService) {
$rootScope.$on('messages.update', function () {
$scope.messageCount = messagesService.getCount();
});
// Manual call, if needed
$scope.getMessageCount = function () {
messagesService.query().then(function (successCallback) {
$scope.messageCount = messagesService.getCount();
});
};
}]);
myApp.controller('DashboardCtrl', [
'$scope',
'$rootScope',
'messagesService',
function ($scope, $rootScope, messagesService) {
$rootScope.$on('messages.update', function () {
$scope.messages = messagesService.all();
});
// Manual call, if needed
$scope.getMessages = function () {
messagesService.query().then(function (successCallback) {
$scope.messages = messagesService.all();
$rootScope.$broadcast('messages.update');
});
}
}]);
You can set cache:true on a $http request. There are numerous ways to data bind within angular without needing to use the $broadcast approach you are using. Also note, $broadcast from a scope will be receievd by all descendent scopes, so no need to inject $rootSCope just for that purpose, can listen on $scope.
Here's one approach that controllers use promise of $http to retrieve data. I used a button click to retrive data for DashControl so can see that request does get cached
myApp.factory('messagesService',function($http) {
return{
query:function query(callback) {
/* return promise of the request*/
return $http.get('messages.json',{ cache:true}).then(function(res){
/* resolve what data to return, can set additional properties of the service here if desired*/
return res.data
}).then(callback);
}
}
});
myApp.controller('HeaderCtrl',function($scope, messagesService) {
messagesService.query(function(messages){
$scope.messagesCount = messages.length;
});
});
myApp.controller('DashboardCtrl', function($scope, messagesService) {
/* use button click to load same data, note in console no http request made*/
$scope.getMessages=function(){
messagesService.query(function(messages){
$scope.messages = messages;
})
}
});
Essentially in this scenario, whatever controller calls the factory service first will generate the data cache
DEMO
I would do it like that:
myApp.factory('messagesService', function() {
var expose = {
messages: []
};
expose.query = function () {
// Would actually hit the database asyncronously
expose.messages = ['one', 'two', 'three', 'four'];
console.log('query');
};
// Initialization
expose.query();
return expose;
}
);
And in your controllers:
$scope.messagesCount = messagesService.messages.length;
Model with broadcasting and pre-hitting database looks heavy for me.
So here is code, that can be embedded in service:
var sv = this;
var deferred = sv.$q.defer();
if (sv._running) {
return sv._running;
}
sv._running = deferred;
It based on reusing promise. To make it query database once - just don't set sv._running to false and it will always return first obtained result.

Better design for passing data to other ng-view's and persisting it across controllers

I started developing in AngularJS. I'm confused as to whether this is a proper design to pass data between my partial views.
Right now I have a loader page where I do some request.
function PeopleController($scope,$http,$location){
$http.get('/location/-79.18925/43.77596').
success(function(data){
$scope.savePeopleResponse(data);
$location.url('/test');
});
}
Then in the view that gets loaded for /test
I am just calling
<div ng-controller="resultController">
<div class="blueitem">{{getResultForPeople()|json}}</div>
</div>
[resultController]
function resultController($scope){
$scope.getResultForPeople = function(){
return $scope.getPeopleResponse();
}
}
and the savePeopleResponse and getResultForPeople are "cached" in the rootScope as such
app.run(function($rootScope) {
var peopleResponse = {};
$rootScope.savePeopleResponse = function(data) {
peopleResponse = data;
console.log(data);
}
$rootScope.getPeopleResponse = function(){
return peopleResponse;
}
});
Now as you can see, this will get very messy if this application grows larger and larger. What's the best way to handle data so that it's persisted across controllers?
You can persist data across controllers by creating your own service as described nicely in this blog. You can also refer to this question.
In your case you can move your savePeopleResponse and getPeopleResponse into a service and then inject the service into any controllers you would like to access it.
angular.module('myApp', [])
.factory('peopleService', function () {
var peopleResponse = {};
return {
savePeopleResponse:function (data) {
peopleResponse = data;
console.log(data);
},
getPeopleResponse:function () {
return peopleResponse;
}
};
});
With your controller something like this:
function resultController ($scope, peopleService) {
$scope.getResultForPeople = peopleService.getPeopleResponse;
}
With this code example make sure you include ng-app="myApp"

Resources