Angular chat client - 2 views with one controller - angularjs

I build chat function in my web app and i am about to create chat functionality between logged clients. Here is my screen from application to show exactly what i want to solve
Screen of my app
As you can see i got list of online users stored in scope in sidebar. Its created as partial view in my Asp.Net with .cshtml and i render content in "white box" using angular routing.
Problem is i use same controller twice and it creates new scope for each html so i got data in my sidebar, but in my content view i dont have any data. I am thinking about passing my data to rootscope, but i dont know if its good idea.
So my question is. Is there anything how i can clone my data from one controller to another or how i can solve this without changing functionality and if i can keep my views controlled with one controller.
Here is my PrivateChatController.js
(function () {
'use strict';
app.controller('PrivateChatController', ['$rootScope', '$scope', 'SignalRService', '$location', 'PrivateChatService', PrivateChatController]);
function PrivateChatController($rootScope, $scope, SignalRService, $location, PrivateChatService) {
//angular stuff
$scope.online_users = [];
$scope.isChatHidden = false;
$scope.openPrivateChatWindow = function (index) {
// $scope.isChatHidden = true;
angular.forEach($scope.online_users, function (value, key) {
if (index == key) {
$rootScope.currentPrivateChatUser = ({
UserName: value.UserName,
ConnectionId: value.connectionId,
});
$location.path("/details/" + value.UserName);
}
});
};
$scope.closePrivateChatWindow = function (index) {
$scope.isChatHidden = false
};
//signalR stuff
var chatHub = $.connection.chatHub;
$.connection.hub.logging = true;
chatHub.client.foo = function () { };
registerClientMethods(chatHub);
$.connection.hub.start()
.done(function(){ console.log('Now connected, connection ID=' + $.connection.hub.id); })
.fail(function () { console.log('Could not Connect!'); });
function registerClientMethods(chatHub) {
//user object
chatHub.client.newOnlineUser = function (user) {
var newUser = ({
connectionId: user.ConnectionId,
UserName: user.UserName
});
$scope.online_users.push(newUser);
$scope.$apply();
};
//compare scope online users with server list of online users
chatHub.client.getOnlineUsers = function (onlineUsers) {
//loop through scope
angular.forEach($scope.online_users, function (scopeValue, scopeKey) {
//loop through received list of online users from server
angular.forEach(onlineUsers, function (serverListValue, serverListKey) {
if (!(serverListValue.ConnectionId == scopeValue.connectionId)) {
var newUser = ({
connectionId: serverListValue.ConnectionId,
UserName: serverListValue.UserName
});
$scope.online_users.push(newUser);
$scope.$apply();
}
})
})
};
chatHub.client.onUserDisconnected = function (id, user) {
var index = 0;
//find out index of user
angular.forEach($scope.online_users, function (value, key) {
if (value.connectionId == id) {
index = key;
}
})
$scope.online_users.splice(index, 1);
$scope.$apply();
};
}};})();

Consider using services as a layer for data sharing. It should also contain chat related logic, in my opinion controllers should be as thin as possible.
Move chatHub.client.getOnlineUsers function to the service and create getter for users.
Further read

Related

Firebase function not running until view change [duplicate]

This question already has an answer here:
How come Angular doesn't update with scope here?
(1 answer)
Closed 6 years ago.
I have a Firebase function inside an angular controller. There is a button that when clicked takes the selected option and the type input and stores them into the user's object like so:
{
selected-option : input-value
}
This works perfectly, but only works when the view is changed. In my case both airlines already have data so this function displays an $ionicPopup.
After the view has changed once the functionality is absolutely perfect. This is obviously a problem and I assume it is an $apply or $digest issue.
Here is my controller code (Supected location marked by "ISSUE RIGHT HERE"):
.controller('ProgramCtrl', ['$scope', '$state', '$firebaseArray', 'facebook', '$ionicPopup', '$ionicLoading',
function($scope, $state, $firebaseArray, facebook, $ionicPopup, $ionicLoading) {
$scope.goBack = function() {
$state.go('app.home');
}
$scope.show = function() {
$ionicLoading.show({
template: 'Loading...'
});
};
$scope.hide = function(){
$ionicLoading.hide();
};
// Get who is logged in
$scope.user = facebook.get();
// Array of airlines
var airRef = ref.child("airlines");
$scope.airlines = $firebaseArray(airRef);
$scope.selectedAir = {};
$scope.miles = {};
$scope.revealInput = function(num) {
// Jquery variables
$milesPoints = $(".milesPoints");
$saveTicket = $(".saveTicket");
// Will fade in visibility depending on num passed into function
switch(num) {
case 1:
$saveTicket.prop("disabled", false);
$saveTicket.fadeTo(400, 1);
break;
default:
break;
}
}
// Add program to user
$scope.addProgram = function () {
// Connect to Firebase
Firebase.goOnline();
// Check for facebook user
if(jQuery.isEmptyObject($scope.user)) {
// Get Firebase user
var authData = ref.getAuth();
var theUser = ref.child("users").child(authData.uid);
var selected = {};
// Access user miles data
// $scope.show();
// ISSUE RIGHT HERE
theUser.child("miles").child($scope.selectedAir.name.$id).once("value", function(snapshot) {
// Update scopes
var exist = snapshot.exists();
// Check if object id exists, if so notify user
if(!exist) {
// Save and update program to user object
selected[$scope.selectedAir.name.$id] = $scope.miles.num;
theUser.child("miles").update(selected);
//$scope.hide();
$state.go("app.saved");
} else {
// Popup alert
var alertPopup = $ionicPopup.alert({
title: 'Oops!',
template: "You already created this airline! Go to the 'Add Ticket' page to add more points."
});
alertPopup.then(function(res) {
console.log("You already created this airline! Go to the 'Add Ticket' page to add more points.");
});
}
})
} else {
var theUser = ref.child("users").child($scope.user.id);
var selected = {};
$scope.show();
theUser.child("miles").child($scope.selectedAir.name.$id).once("value", function(snapshot) {
var exist = snapshot.exists();
if(!exist) {
selected[$scope.selectedAir.name.$id] = $scope.miles.num;
theUser.child("miles").update(selected);
$scope.hide();
$state.go("app.saved");
} else {
var alertPopup = $ionicPopup.alert({
title: 'Oops!',
template: "You already created this airline! Go to the 'Add Ticket' page to add more points."
});
alertPopup.then(function(res) {
console.log("You already created this airline! Go to the 'Add Ticket' page to add more points.");
});
}
})
}
}
}])
Thanks for the help and I can provide more code or screenshots if needed.
The issue is in this piece of code:
theUser.child("miles").child($scope.selectedAir.name.$id).once("value", function(snapshot) {
$timout(function() {
var exist = snapshot.exists();
// Check if object id exists, if so notify user
if(!exist) {
// Save and update program to user object
selected[$scope.selectedAir.name.$id] = $scope.miles.num;
theUser.child("miles").update(selected);
//$scope.hide();
$state.go("app.saved");
} else {
// Popup alert
var alertPopup = $ionicPopup.alert({
title: 'Oops!',
template: "You already created this airline! Go to the 'Add Ticket' page to add more points."
});
alertPopup.then(function(res) {
console.log("You already created this airline! Go to the 'Add Ticket' page to add more points.");
});
}
});
})
When you call once(), it starts loading data from Firebase. Since this may take some time, you pass in a callback function that is invoked when the data is available. But by the time the callback function is invoked, AngularJS is not expecting updates to the $scope anymore.
The solution is to wrap the code into a $timeout(), which ensures it gets executed when AngularJS is ready to handle scope changes again:
theUser.child("miles").child($scope.selectedAir.name.$id).once("value", function(snapshot) {
// Update scopes
var exist = snapshot.exists();
// Check if object id exists, if so notify user
if(!exist) {
// Save and update program to user object
selected[$scope.selectedAir.name.$id] = $scope.miles.num;
theUser.child("miles").update(selected);
//$scope.hide();
$state.go("app.saved");
} else {
// Popup alert
var alertPopup = $ionicPopup.alert({
title: 'Oops!',
template: "You already created this airline! Go to the 'Add Ticket' page to add more points."
});
alertPopup.then(function(res) {
console.log("You already created this airline! Go to the 'Add Ticket' page to add more points.");
});
}
})
Note that this problem wouldn't happen if you used AngularFire's $firebaseObject() and $firebaseArray() primitives, since those automatically notify AngularJS of scope changes.
We get this question a lot. Here's a recent one: Taking long to load

AngularJS Data sharing between two controllers

I have a issue in sharing data between 2 controllers and 2 views. I have 2 views. I created 2 separate controllers and bind with 2 different views. Now I have 2 share data between 2 controllers so I created a service. Issues is one controller get data from remote source and other controller is consuming that data. But the view that consumes data loads first, so pulled data from remote source is not exactly utilize by first one. Eg.
//My Services
as.service('songGenreService', function () {
var genreList = [];
var addGenres = function (newObj) {
genreList = newObj;
};
var getGenres = function () {
return genreList;
};
return {
addGenres: addGenres,
getGenres: getGenres
};
});
as.controller('SongListController', ['$scope', 'Song', "$stateParams", 'Category', 'Album', 'base64', 'langConversion', 'CONFIG', 'Poet', "songGenreService",
function ($scope, Song, $stateParams, Category, Album, base64, langConversion, CONFIG, Poet, songGenreService) {
$scope.getCategories = function () {
Category.find({
filter: {
fields: ["id", "name"],
where: {
parentId: 2
}
}
}).$promise.then(function (categories) {
$scope.categories = categories;// Here I am giving data to other source.
songGenreService.addGenres(categories);
$scope.genreId = $scope.categories[0].id;
$scope.genreName = $scope.categories[0].name;
});
}();
}
]);
as.controller('SongGenreController', ['$scope', 'Song', "songGenreService",
function ($scope, Song, songGenreService) {
$scope.categories = songGenreService.getGenres();
console.log($scope.categories);
}
]);
Issue is "SongGenreController" loads first because of HTML as it loads first. I wish to populate it when data loads successfully. "songGenreService.getGenres();" doesn't run with remote source.
The way I fixed a similar issue is by using a publish subscribe mechanism.
In your service you can put a publish when genres are added like so:
var addGenres = function (newObj) {
genreList = newObj;
$rootScope.$broadcast('genresUpdated, genreList);
};
then in your two controllers you subscribe to the event :
$scope.$on('genresUpdated', function(event, genreList){
$scope.genres = genreList;
// and other code you want to have triggered when the genreList changes
});

Using AngularJS Service to Check if User has Admin Permissions

I am building an SharePoint App using AngularJS and am attempting to define a service that retrieves if the user is an Admin or not. The service itself is successfully logging/working as expected, but I am not sure how to use this in a controller. My end goal is that when a page loads that is tied to a controller, that this service checks if they are an admin or not. From that point, I can do all sorts of magic (ex. redirect, etc.). Here is my service:
// Check if user is an admin
appServices.factory('appAdminCheck', ['$resource', 'appCurrentUserProfile', 'appAdmins', function ($resource, appCurrentUserProfile, appAdmins) {
var userAdmin = [];
appCurrentUserProfile.query(function (usercheck) {
var userID = usercheck.Id;
appAdmins.query(function (admins) {
var admins = admins.value; // Data is within an object of "value", so this pushes the server side array into the $scope array
// Foreach type, push values into types array
angular.forEach(admins, function (adminvalue, adminkey) {
if (adminvalue.Admin_x0020_NameId == userID) {
userAdmin = true;
console.log("I'm an Admin" + userAdmin);
}
});
});
});
return userAdmin;
}]);
Update: Upon closer inspection, I would like to return the array of values, but it keeps stating that the array length is 0. I am sure it is because I am not "returning" properly.
Here is my updated service:
appServices.factory('appAdminCheck', ['$resource', 'appCurrentUserProfile', 'appAdmins', function ($resource, appCurrentUserProfile, appAdmins) {
var userAdmin = [];
var checkUser = function() {
appCurrentUserProfile.query(function (usercheck) {
var userID = usercheck.Id;
appAdmins.query(function (admins) {
var admins = admins.value; // Data is within an object of "value", so this pushes the server side array into the $scope array
// Foreach type, push values into types array
angular.forEach(admins, function (adminvalue, adminkey) {
if (adminvalue.Admin_x0020_NameId == userID) {
userAdmin.push({
isAdmin: 'Yes',
role: adminvalue.Role,
});
}
});
});
});
return userAdmin;
}
return {
checkUser: checkUser
};
}]);
Here is a logging call in a controller:
var test = appAdminCheck.checkUser();
console.log(test);
Seeing as there appears to be some asynchronous actions happening, you'll want to return a promise. You can do this by chaining the then promise resolution callbacks from your other services (assuming they're $resource instances or similar). For example...
appServices.factory('appAdminCheck', function (appCurrentUserProfile, appAdmins) {
return function() {
return appCurrentUserProfile.query().$promise.then(function(usercheck) {
return appAdmins.query().$promise.then(function(admins) {
// this needs to change if admins.value is not an array
for (var i = 0, l = admins.value.length; i < l; i++) {
if (admins.value[i].Admin_x0020_NameId === usercheck.Id) {
return true;
}
}
return false;
});
});
};
});
Then, you can use this promise resolution in your controller, eg
appAdminCheck().then(function(isAdmin) {
// isAdmin is true or false
});

angularjs singleton doesn't work

In app.js I have a variable that I use in two files/controllers:
var app = angular.module('appDemo', ['MainControllers', 'MainServices'])
.constant('myConfig', {
'backend': 'http://localhost:1236'
})
.service('mailService', function() {
var mail = {
value: 'hello world'
};
var getMail = function() {
return mail;
}
var setMail = function(email) {
mail.value = email;
}
return {
getMail: getMail,
setMail: setMail
};
);
Setting the variable from controllerOne goes fine:
angular.module('MainControllers')
.controller('MemberController', function ($scope, mainService, appDemo) {
window.onbeforeunload = function (e) {
appDemo.setMail('test#test.com');
};
But when I get the setting variable from the controllerTwo than I get the default value:
angular.module('MainControllers')
.controller('EmailController', function($scope, appDemo) {
$scope.mailAddress = appDemo.getMail();
});
Each controller is in separate file.
what is wrong?
This may be because the service itself is being reloaded because as I can see you are setting the mail in the first controller on onbeforeunload.
Services can't persist on window reloads or page refresh. They get reloaded hence reinitialized every time you reload the page.
If you want to persist the values try putting it in localStorage or sessionStorage.

When page refresh, how to call back function in angularjs

I'm trying to maintain session after page refresh in angularjs using java. I have seen many examples but, i didn't find proper solution which exactly I'm looking for.
Please find below login snippet code, when i click on login button it is calling loginUser()function in LoginController
When i do page refresh it is going to LoginController but it is not going inside loginUser()function.
According to my knowledge until we call function, it doesn't goes inside of it.
When I do refresh how can i call back loginUser() function.
please help me out from these. Appreciated..Many thanks.
LoginController.js
function LoginController($scope, $http, $location, $rootScope,
userService, SessionIdService) {
$scope.user = {};
$scope.user.username = '';
$scope.user.password = '';
$rootScope.loginUser = function(username, password) {
$scope.resetError();
$http.post('/user/main/login/' + username, password).success(
function(login) {
if (login.sessionId === null) {
$scope.setError(login.status);
return;
} else {
$rootScope.userlogin = login.uname;
userService.setUserName(login.uname);
SessionIdService.setSessionId(login.sessionId);
$location.path("/home");
}
}).error(function() {
$scope.setError('Invalid user/password combination');
});
};
$scope.resetError = function() {
$scope.error = false;
$scope.errorMessage = '';
};
$scope.setError = function(message) {
$scope.error = true;
$scope.errorMessage = message;
$rootScope.sees = '';
$rootScope.userlogin = '';
};
};
app.js
app.run(function($rootScope, $location, SessionIdService) {
$rootScope.$on("$routeChangeStart", function(event, next, current) {
console.log("Routechanged... ");
if (SessionIdService.getSessionId == "true") {
if (next.templateUrl == "scripts/views/homescreen.html") {
$location.path("/home");
} else {
$location.path("/screen");
}
}
});
});
login.html
<input name="textfield" type="text" ng-model="user.username"/>
<input name="textfield" type="password" ng-model="user.password"/>
<button type="button" ng-lick="loginUser(user.username,user.password)">Login</button>
It is not clear to me why you want to call loginUser after page refresh. Isn't the user already logged in? I think what you want is to call the success function inside the loginIser. In that case, you need to embed that data as a global JS variable inside your Java template, and pass that to your controller somehow.
You probably want these to be run after refresh:
$rootScope.userlogin = login.uname;
userService.setUserName(login.uname);
SessionIdService.setSessionId(login.sessionId);
$location.path("/home");
So, in your Java template, do something like:
<script>window.UNAME = {% this comes from your database %};window.SESSIONID={% similar %}</script>
Then, call that function somehow with window.UNAME as input. (or in your controller, check for the existence of window.UNAME and call it immediately. Something like:
window.UNAME && function (name, sessionId) {
$rootScope.userlogin = uname;
userService.setUserName(uname);
SessionIdService.setSessionId(sessionId);
$location.path("/home");
) {
}(window.UNAME, window.SESSION_ID)
Some other recommendations (unrelated to your main problem probably):
First of, change $rootScope.loginUser = function(username, password) { to
$rootScope.loginUser = function() {
var username = $scope.user.username;
var password = $scope.user.password
since you already have access to username and password there. So, change ng-click="loginUser()".
Second, SessionIdService.getSessionId == "true" seems off, check should probably be just SessionIdService.getSessionId

Resources