Pass Data to Service from Controller AFTER receiving info from Server - angularjs

I need to pass data to an array in service after getting from server. The controller runs the function to retrieve data as shown
.controller("messagesController", function($scope, $stateParams, RegisterP) {
// function here retrives data from the RegisterP parameter above calling another service
$scope.messagepool = [ 1, 2]; //I add the data I get here to an array
})
This array then gets sent to a service
.service('ChatService', function() {
return {
chats: [
{
id: "1",
message: "Chat Message 1"
}
],
getChats: function() {
return this.chats;
},
getChat: function(chatId) {
for(i=0;i<this.chats.length;i++){
if(this.chats[i].id == chatId){
return this.chats[i];
}
}
}
}
})
That in turn sends that to a view/views. I need to know how to send the info from controller so its the one occupying the chats: [] so the the views are updated in REAL-TIME. Using the Ionic Framework.
Bonus: I haven't researched having the get function in controllers constantly poll the incoming messages however if you can tell me that it'll be helpful and save time.

controller
.controller("messagesController", function($scope, $stateParams, RegisterP,ChatService) {
// function here retrives data from the RegisterP parameter above calling another service
$scope.messagepool = [ 1, 2]; //I add the data I get here to an array
ChatService.sendData($scope.messagepool);
});
service
.service('ChatService', function() {
return {
sendData:function(data){
this.chatData=data;
console.log(this.chatData);
// this.getChats(); you can call service function from here
},
getChats: function() {
console.log(this.chatData); // it will work here too
return this.chats;
},
getChat: function(chatId) {
for(i=0;i<this.chats.length;i++){
if(this.chats[i].id == chatId){
return this.chats[i];
}
}
}
}
});
Hope it will help you :) Thanks

Related

AngularJs - keeps getting old value of scope even if it is updated

In my application, I'm retrieving some fields from the database and setting the values in local storage when a user logs in.
and then retrieving from local storage to display it to user:
if (localStorage.getItem('a') != undefined) {
$rootScope.a = localStorage.getItem('a');
}
So this is working fine. But the problem is when the value gets updated in the database and user logs in after logging out, then even if the local storage has correct value (i.e., recently updated value), the first time it will display the old value of the scope variable which just got updated.
I tried $apply() and also $digest() as suggested in different posts here :
$timeout( function () {
$scope.$apply( function () {
$rootScope.a = localStorage.getItem('a');
});
});
But it didn't work out. It always displays the old value of scope.
It will only display the new value after reloading the page once.
P.S. - The web page in my application won't be reloaded in any module, even when logging in and out.
You can try watching for the scope variable like this:
$rootScope.$watch('a', function (newVal, oldVal){
if newVal != oldVal
$rootScope.a = newVal;
}
Something else to try is to change 'a' from string to object as I think that angular watches for values using object reference.
here's some useful reference for $watch
http://www.learn-angular.org/#!/lessons/watch
https://www.bennadel.com/blog/2852-understanding-how-to-use-scope-watch-with-controller-as-in-angularjs.htm
Hope it helps in any way
EDIT
ok I tested it. You don't need watch neither $apply if you refresh the scope when data refreshing.
Here's what I've done:
(function() {
angular.module('myapp', []).controller('myctrl', [
'$scope', function($scope) {
var data, getDataFromLocalStorage;
console.log("scope is ", $scope);
getDataFromLocalStorage = function() {
return JSON.parse(localStorage.getItem('data'));
};
data = [
{
id: 1,
text: "test1"
}, {
id: 2,
text: "test2"
}, {
id: 3,
text: "test3"
}
];
localStorage.setItem('data', JSON.stringify(data));
$scope.myData = getDataFromLocalStorage();
return $scope.changeData = function() {
var dataNew;
dataNew = [
{
id: 4,
text: 'text4'
}, {
id: 5,
text: 'text5'
}, {
id: 6,
text: 'text6'
}
];
localStorage.setItem('data', JSON.stringify(dataNew));
return $scope.myData = getDataFromLocalStorage();
};
}
]);
}).call(this);
https://codepen.io/NickHG/pen/rzvGGx?editors=1010

Passing Data from Service to Template via Controller and $stateParams

I have a service with the following code:
.service('ChatService', function() {
return { //Gets Data from controller
sendData: function(data) {
this.chatData = data;
console.log('this.chatData: '+this.chatData);
},
chats: this.chatData,
getChats: function() {
return this.chatData;
},
getChat: function(chatId) {
for(i=0; i<this.chats.length; i++) {
if (this.chats[i].id == chatId) {
return this.chats[i];
}
}
}
}
})
The Important thing here is that sendData retrieves info
[{id: 1, message: "Chat Message 1"},{id: 2, message: "Message 2"}]
from the controller. The getChats in Services is then called $scope.chats = ChatService.getChats(); by the same controller to show in the template.
When that item is clicked a new page with more info is to show hence the getChat and getChats function in the service.
The code in the controller for the page to load more details is
$scope.chatId = $stateParams.chatId;
$scope.chat = ChatService.getChat($scope.chatId);
However I'm getting error cannot read property length of undefined. Now if I change the chats: this.ChatData in the services to
chats: [{id: 1, message: "Chat Message 1"},{id: 2, message: "Message 2"}]
It works like a charm but I need it to display in real time what is in the controller because I'm downloading and receiving info from the server that will be updated in real time for a chat app.
Use angular.copy to update the reference:
app.service('ChatService', function() {
//Gets Data from controller
this.sendData = function(data) {
//this.chatData = data;
//console.log('this.chatData: '+this.chatData);
//Use angular.copy
angular.copy(data, this.chats);
};
this.chats = [];
this.getChats = function() {
return this.chats;
};
this.getChat = function(chatId) {
for(i=0; i<this.chats.length; i++) {
if (this.chats[i].id == chatId) {
return this.chats[i];
};
};
};
});
By using the angular.copy to update the array reference, controllers that have previously fetched the reference will get updated.
For more information, see AngularJS angular.copy API Reference.

Array populated witin 'service' but empty when referenced by any 'controller'

I have an AngularJS service which should get a JSON object and create three arrays based on differing criteria (all, searchable and has coordinates). These arrays need to be referenced by more than one controller, hence the use of a service.
When I test any of the three arrays the array within the service itself (as below), all three are correctly populated.
However, all three of my arrays are empty when referenced by any controller.
What am I missing here?
app.service('$stationsList', ['$http', function($http){
var stationsList = [],
searchableStations = [],
locatableStations = [];
$http.get('stations.json').then(function(res){ // Grab the JSON list of all stations
[].map.call(res.data || [], function(elm){ // Map all stations...
stationsList = res.data; // Set all stations to 'stationsList'
if(elm.link.indexOf(".xml") > -1) // Check to see if the station is searchable (has a full link)
searchableStations.push(elm); // It does - add the station to 'searchableStations'
if( // Check to see if the station can be checked as the closest station (has coordinates)
isFinite(parseFloat(elm.latitude)) &&
isFinite(parseFloat(elm.longitude))
)
locatableStations.push(elm); // It does - add the station to 'locatableStations'
});
console.log(stationsList);
console.log(searchableStations);
console.log(locatableStations);
});
return{
getList: function(){
return stationsList;
},
setList: function(value){
stationsList = value;
},
getSearchable: function(){
return searchableStations;
},
setSearchable: function(value){
searchableStations = value;
},
getLocatable: function(){
return locatableStations;
},
setLocatable: function(value){
locatableStations = value;
}
};
}]);
Example of how I'm referencing service -
app.controller('searchCtrl', ['$scope', '$http', '$localStorage', '$stationsList', function($scope, $http, $localStorage, $stationsList){
$scope.stationsList = $stationsList.getSearchable(); // Grab a list of all stations
$scope.selectStation = click_selectStation; // Handle clicks of a station within the 'searchCtrl' controller
$scope.localStorage = $localStorage.$default({ // Grab the local storage (so that it can be updated when the user selects a station)
recentStations: [] // Set a default value of '[]' for recentStations in case it doesn't exist
});
}]);
Edit
Derived from the answer posted by PankajParkar below, here is the service that will return the three arrays that I require.
However, my issue here is that every call to a method within the service triggers another async call to $http.get my JSON data. This is exactly what I was trying to avoid by using a service.
My desired outcome is one JSON call per page load, with my 3 arrays being created from that JSON call and then accessible to my controllers as and when required. If a service is not the correct answer, I am certainly open to other suggestions.
app.service('$stationsList', ['$http', function($http){
var searchableStations = [],
locatableStations = [];
/**
* Grab all stations (for the master list)
*/
var getAllStations = function(){
return $http.get('stations.json').then(function(res){ // Grab the JSON list of all stations
return res.data;
});
};
/**
* Grab only searchable stations (those with full links)
*/
var getSearchableStations = function(){
return $http.get('stations.json').then(function(res){ // Grab the JSON list of all stations
[].map.call(res.data || [], function(elm){ // Map all stations...
if (elm.link.indexOf(".xml") > -1) // Check to see if the station is searchable
searchableStations.push(elm); // It is - add the station to 'searchableStations'
});
return searchableStations;
});
};
/**
* Grab only locatable stations (those with coordinates)
*/
var getLocatableStations = function(){
return $http.get('stations.json').then(function(res){ // Grab the JSON list of all stations
[].map.call(res.data || [], function(elm){ // Map all stations...
if(
isFinite(parseFloat(elm.latitude)) &&
isFinite(parseFloat(elm.longitude))
) // Check to see if the station is locatable
locatableStations.push(elm); // It is - add the station to 'locatableStations'
});
return locatableStations;
});
};
return{
getAll: getAllStations,
getSearchable: getSearchableStations,
getLocatable: getLocatableStations
};
}]);
Your current code is failing because you made asynchronous ajax call & accepting value as soon as it made. That's why you are getting your values as undefined.
You need to wait till your ajax gets completed, that could be implemented using returning ajax promise to controller from service. So i'd suggest you to create a new method which will do $http ajax and will return promise from that function & that will execute .then function of controller that called the getSearchableStations. Below snippet will give you an Idea what I wanted to say.
Service
app.service('$stationsList', ['$http', function($http) {
var stationsList = [],
searchableStations = [],
locatableStations = [];
var getSearchableStations = function() {
return $http.get('stations.json').then(function(res) { // Grab the JSON list of all stations
[].map.call(res.data || [], function(elm) { // Map all stations...
stationsList = res.data; // Set all stations to 'stationsList'
if (elm.link.indexOf(".xml") > -1) // Check to see if the station is searchable (has a full link)
searchableStations.push(elm); // It does - add the station to 'searchableStations'
if ( // Check to see if the station can be checked as the closest station (has coordinates)
isFinite(parseFloat(elm.latitude)) &&
isFinite(parseFloat(elm.longitude))
)
locatableStations.push(elm); // It does - add the station to 'locatableStations'
});
console.log(stationsList);
console.log(searchableStations);
console.log(locatableStations);
return locatableStations; //return data from here.
});
};
return {
getList: function() {
return stationsList;
},
setList: function(value) {
stationsList = value;
},
getSearchable: function() {
return searchableStations;
},
setSearchable: function(value) {
searchableStations = value;
},
getLocatable: function() {
return locatableStations;
},
setLocatable: function(value) {
locatableStations = value;
},
//added new function
getSearchableStations: getSearchableStations
};
}]);
Inside you controller you will call service getSearchableStations method that does return promise, You will use .then function that would get called when promise get resolved. Same has been shown below with code.
Controller
app.controller('searchCtrl', ['$scope', '$http', '$localStorage', '$stationsList',
function($scope, $http, $localStorage, $stationsList){
$stationsList.getSearchableStations().then(function(data){
$scope.stationsList = data;
$scope.selectStation = click_selectStation; // Handle clicks of a station within the 'searchCtrl' controller
$scope.localStorage = $localStorage.$default({ // Grab the local storage (so that it can be updated when the user selects a station)
recentStations: [] // Set a default value of '[]' for recentStations in case it doesn't exist
});
}); // Grab a list of all stations
}]);

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
});

Resources