I have two angular services that need to share models (a list of messages and an individual message), which they get from a call to our API. The service is as follows:
angular.module('CmServices', ['ngResource'])
.factory('Messages', function ($resource, $routeParams, $rootScope) {
var data = {};
data.rest = $resource(url, {}, {
query: {method:'GET', params: params},
post: {method:'POST', params: params}
});
// Trying to set this through a call to the API (needs to get param from route)
$rootScope.$on('$routeChangeSuccess', function(event, current, previous) {
var messages = data.rest.query({m_gid: $routeParams.gid}, function () {
data.messages = messages;
});
});
return data;
});
and the controllers are:
function MessagesCtrl ($scope, $http, $location, $routeParams, Messages) {
$scope.messages = Messages.messages;
}
function MessageCtrl ($scope, $http, $location, $routeParams, Messages) {
$scope.messages = Messages.messages[0];
}
But neither of the controllers update when the data loads from the REST API (I've logged the data coming back, and it definately does).
Instead of assigning a new array to data.messages like this:
data.messages = messages
use angular.copy() instead, which will populate the same array:
angular.copy(messages, data.messages)
That way, the controllers will see the update.
The problem is that you are returning a different version of data to each controller. I would place messages in $rootScope. So
data.rest.query({m_gid: $routeParams.gid}, function () {
$rootScope.messages = messages;
});
Incidentally, what is the purpose of setting the return value of data.rest.query to var messages? That variable gets blown as soon as you leave the function.
Related
my factory is:
myAppServices.factory('ProfileData',['$http', function($http){
return{
newly_joined:function(callback){
$http.get(
//myUrl will be an url from controller.
myUrl
).success(callback);
}
};
}
]);
and I have three controller which has different URL:
controller1:
AppControllers.controller('ProfileListCtrl',['$scope','$state', '$rootScope', 'ProfileData', '$timeout', function($scope, $state, $rootScope, ProfileData, $timeout ) {
ProfileData.newly_joined(function(response) {
var myUrl= "www.abc...."
//something goes there
});
}]);
controller2:
AppControllers.controller('ProfileListCtrl1',['$scope','$state', '$rootScope', 'ProfileData', '$timeout', function($scope, $state, $rootScope, ProfileData, $timeout ) {
ProfileData.newly_joined(function(response) {
var myUrl= "www.abc...."
//something goes there
});
}]);
and controller 3 is:
AppControllers.controller('ProfileListCtrl2',['$scope','$state', '$rootScope', 'ProfileData', '$timeout', function($scope, $state, $rootScope, ProfileData, $timeout ) {
ProfileData.newly_joined(function(response) {
var myUrl= "www.abc...."
//something goes there
});
}]);
I want different data in different controller because of different URL and I am showing all three details on single web page.
So if there were any method to send 'myUrl' in factory that I can use that for pulling data.
Note: please don't suggest me for using $resource or $routeparams because $resource was not successfull in pulling data from json and I don't want to use big variable Url for my page.
Thanks in advance
All you need to do is add an additional parameter to the newly_joined function:
newly_joined:function(callback, myUrl){
Also, you should be using .then instead of .success
Your factory should be returning promises instead of using callbacks.
myAppServices.factory('ProfileData',['$http', function($http){
return function(myUrl) {
return $http.get(myUrl);
};
}]);
The controller
AppControllers.controller('ProfileListCtrl',['$scope', 'ProfileData', function($scope,ProfileData) {
var myUrl= "www.abc....";
var httpPromise = ProfileData(myUrl);
httpPromise.then(function onFulfilled(response) {
$scope.data = response.data;
}).catch(function onRejected(response) {
console.log("ERROR ", response.status);
});
}]);
The DEMO on JSFiddle
The advantage of using promises is that they retain error information.
Also notice that myUrl is sent to the factory as an argument.
For more information on the advantages of using promises, see Why are Callbacks from Promise Then Methods an Anti-Pattern?
I'm trying to do the following.
Angular controller calls and MVC controller GET method. This method then calls into a REST API on the web which returns a list of configuration items. I then turn this into a dictionary so I can look up configuration values based on the key, and then I want to pass this back to the Angular controller and store it there in a variable that I can access from many different scenarios, eg displaying them in grids, updating values, changing and updating them back to the REST API etc. I have tried to set up the pipes but I can't seem to get the data in a readable/usable format in the Angular controller.
My controller
app.controller("SEFlexHomeController", ["$scope", "$http", "$modal", "$log", "$element", "$rootScope", "AlertsService", "AuthService", "SEApplicationService", function ($scope, $http, $modal, $log, $element, $rootScope, AlertsService, AuthService, SEApplicationService) {
$rootScope.closeAlert = AlertsService.closeAlert;
$scope.isDataLoading = false;
$scope.AuthService = AuthService;
$scope.configvalues = angular.fromJson(SEApplicationService.getCloudConfigParams());
}
]);
My Angular Service
app.factory("SEApplicationService", ["$log", "$http", "$timeout", function($log, $http, $timeout) {
var appService = {};
appService.getCloudConfigParams = function () {
return $http.get("/SEFlex/SEFlexAdmin/GetCloudConfigValues");
}
return appService;
}]);
My MVC controller
public ActionResult GetCloudConfigValues()
{
try
{
var helper = new ApplicationServiceHelper();
var dictionary = helper.GetCloudConfigValues()
.ToList()
.ToDictionary(item => item.ConfigKey, item => item.ConfigValue);
var returnData = JsonConvert.SerializeObject(dictionary);
return Json(new
{
success = true,
data = returnData
}, JsonRequestBehavior.AllowGet);
}
catch (Exception exception)
{
return Json(new
{
success = false,
errors = new[] { exception.Message }
});
}
}
I can confirm at the time of creating the Dictionary in the MVC controller, the dictionary looks as expected for a .NET dictionary. What do I need to do to convert this either before transmission back or back in Angular, so that I can access it in angular as
$scope.configvalues["keyName"]
The $http.get() call returns a promise. You should change the
$scope.configvalues = angular.fromJson(SEApplicationService.getCloudConfigParams());
to
SEApplicationService.getCloudConfigParams().then(function(config) {
$scope.configvalues = config;
});
I'm pretty new to angularjs and javascript so I'm hoping you can help me figure this out. I have a factory creating a service singleton and I want it to subscribe to some events and update itself when those occur.
However, I'm not sure how to get a reference to the object created by the factory in this context. See "My Problem" in a code comment.
I would also appreciate any and all feedback on the way I'm using angular/js and what I could be doing better.
(function () {
'use strict';
var coreMod = angular.module('CoreMod',['ng']);
coreMod.factory('accountService', accountService);
accountService.$inject=['$rootScope',
'$log',
'$http',
'$q',
'$localStorage',
'$sessionStorage',
'authService'];
function accountService($rootScope, $log, $http, $q, $localStorage, $sessionStorage, authService) {
var accountService = {
CurrentAccount: null,
logOut: logOut,
_logIn: userLoggedIn//anyway to hide this?
};
$rootScope.$on('userLoggedIn', accountService._logIn);
return accountService;
function userLoggedIn() {
$http({ method: 'get', url: msApiUrl + '/account/userinfo', timeout: 3000, warningAfter: 50 })//TODO: find a way to make these timinings default
.success(function (result) {
$log.info('User logs into api server successfully and gets response: ' + result);
handleLoginMessage(result);
}).error(function (result) {
$log.warn('Error logging into api server. Response: ' + result);
$rootScope.$broadcast('CriticalError', 'Error logging into api. Please clear your cache and try again. If this occurrs again please contact your system administrator. ');//TODO: configurable and localized message
});
};
function handleLoginMessage(message) {
var accountService = this; //my problem: this is undefined
accountService.CurrentAccount={};
accountService.CurrentAccount.emailHash = message.EmailHash;//TODO: are these case sensitive / can I control that
accountService.CurrentAccount.organizationId = message.OrganizationId;
accountService.CurrentAccount.username = message.Username;
$localStorage.userInfo = accountService;
};
function logOut() {
authService.logOut();
$sessionStorage.$reset();
$localStorage.userInfo = null;
};
}
}());
You don't need the code line at all. Just remove:
var accountService = this;
accountService is an object literal that you have created with var accountService = {...} and your function handleLoginMessage is in the same scope. So the accountService variable should be accessible with-in handleLoginMessage.
If you want to hide _logIn: userLoggedIn then remove it from accountService object but then it is not accessible from outside of your service. Everything that's inside of accountService will be exposed to other controllers, services or factories that are injecting your factory.
I'm building my first MEAN twitter like application and currently try to display a list of posts to the gui. what I currently do is:
The angular.js part:
in my main.js:
angular.module('MyApp')
.controller('MainCtrl', ['$scope', 'Feed', function($scope, Feed) {
$scope.feeds = Feed.showFeeds();
// ...
}
in my feed.js:
angular.module('MyApp')
.factory('Feed', ['$http', '$location', '$rootScope', '$cookieStore', '$alert', '$resource',
function ($http, $location, $rootScope, $cookieStore, $alert, $resource) {
return {
// other functions like addFeed: function(f) {...},
showFeeds: function() {
return $http.get('/api/feeds');
}
The node.js part:
app.get('/api/feeds', function (req, res, next) {
var query = Feed.find();
query.limit(8);
query.exec(function (err, feeds) {
if (err) return next(err);
res.send(feeds)
// feeds is a corret JSON with all my data at this point
});
});
and my home.html:
<p>{{ feeds }}</p> <!-- for testing - this just returns {} -->
<div ng-repeat="feed in feeds">
<div>
{{feed.feedMessage}}
</div>
So my problem is: Everything loads fine, but nothing renders out on the page, I don't get any errors, just my $scope.feeds object is empty. I'm pretty new to this, so maybe it's an obvious bug, but if someone could point me in the right direction, that would be great!
Right now you are returning a promise, and you need to be accessing the data.:
Feed.showFeeds().success( function(data) {
$scope.feeds = data.feeds;
});
The '$http' service provided by angular return always an instance of '$q', another angular service which allows us to use the Promise syntax for all async commands.
When you assign the return of Feed.showFeeds() to your scope variable, you bind a promise to your view and Angular can't display it.
You should use the success method provide by $http in order to get the server data and bind them to your scope variable like bencripps said.
Note: The success method (and error) are specific methods of $http and call the $digest method of angular which triggers a refresh of the view automatically.
angular.module('MyApp')
.controller('MainCtrl', ['$scope', 'Feed', function($scope, Feed) {
$scope.feeds = [];
Feed.showFeeds().success(function(data) {
//depends of the json return
//try a console.log(data)
$scope.feeds = data.feeds
});
// ...
}
From this stackoverflow question, my understanding is that I should be using services to pass data between controllers.
However, as seen in my example JSFiddle, I am having trouble listening to changes to my service when it is modified across controllers.
angular.module('myApp', [])
.controller('Ctrl1', function ($scope, App) {
$scope.status = App.data.status;
$scope.$watch('App.data.status', function() {
$scope.status = App.data.status;
});
})
.controller('Ctrl2', function ($scope, App) {
$scope.status = App.data.status;
$scope.$watch('status', function() {
App.data.status = $scope.status;
});
})
.service('App', function () {
this.data = {};
this.data.status = 'Good';
});
In my example, I am trying to subscribe to App.data.status in Ctrl1, and I am trying to publish data from Ctrl1 to App. However, if you try to change the input box in the div associated with Ctrl2, the text does not change across the controller boundary across to Ctrl1.
http://jsfiddle.net/VP4d5/2/
Here's an updated fiddle. Basically if you're going to share the same data object between two controllers from a service you just need to use an object of some sort aside from a string or javascript primitive. In this case I'm just using a regular Object {} to share the data between the two controllers.
The JS
angular.module('myApp', [])
.controller('Ctrl1', function ($scope, App) {
$scope.localData1 = App.data;
})
.controller('Ctrl2', function ($scope, App) {
$scope.localData2 = App.data;
})
.service('App', function () {
this.data = {status:'Good'};
});
The HTML
<div ng-controller="Ctrl1">
<div> Ctrl1 Status is: {{status}}
</div>
<div>
<input type="text" ng-model="localData1.status" />
</div>
<div ng-controller="Ctrl2">Ctrl2 Status is: {{status}}
<div>
<input type="text" ng-model="localData2.status" />
</div>
</div>
Nothing wrong with using a service here but if the only purpose is to have a shared object across the app then I think using .value makes a bit more sense. If this service will have functions for interacting with endpoints and the data be sure to use angular.copy to update the object properties instead of using = which will replace the service's local reference but won't be reflected in the controllers.
http://jsfiddle.net/VP4d5/3/
The modified JS using .value
angular.module('myApp', [])
.controller('Ctrl1', function ($scope, sharedObject) {
$scope.localData1 = sharedObject;
})
.controller('Ctrl2', function ($scope, sharedObject) {
$scope.localData2 = sharedObject;
})
.value("sharedObject", {status:'Awesome'});
I agree with #shaunhusain, but I think that you would be better off using a factory instead of a service:
angular.module('myApp', [])
.controller('Ctrl1', function ($scope, App) {
$scope.localData1 = App.data;
})
.controller('Ctrl2', function ($scope, App) {
$scope.localData2 = App.data;
})
.factory('App', function () {
var sharedObj = {
data : {
status: 'Good'
}
};
return sharedObj;
});
Here are some information that might help you understand the differences between a factory and a service: When creating service method what's the difference between module.service and module.factory