Hi I have got one question.
I have got one object as following in my Factory
User: {
EmailAddress: ""
}
whenever i make http call I want to update that User.EmailAddress whith returned value. What is the best way of doing it in within the factory? so that at controller level I can just bind my $scope.Email to factory variable. This is what I am doing right now
GetLogOnModel: function () {
if ($location.path().indexOf("login") == 1) {
var promise = $http.get(config.headers.url + "LogOn").then(function (response) {
// The return value gets picked up by the then in the controller.
User.EmailAddress=response.data.Email;
return response.data
});
return promise;
// Return the promise to the controller
}
}
And in Controller
AccountFactory.GetLogOnModel().then(function (data) {
$scope.logOnModel = data;
}, function (err) {
console.log(err.reason);
alert(err.reason);
});
Primitive types (such as strings) are not bound by reference. So you can't bind a scope property to EmailAddress directly and expect it to get automatically updated.
Objects on the other hand are bound by reference, so you could do something like this:
app.factory('AccountFactory', function (...) {
...
var User = {
...
EmailAddress: null
};
function getLogOnModel() {
$http.get(...).then(function (response) {
User.EmailAddress = response.data.Email;
});
}
// Init model (or leave it for the controller to init it)
getLogOnModel();
return {
...
User: User,
getLogOnModel: getLogOnModel
};
});
app.controller('someCtrl', function (..., AccountFactory) {
$scope.user = AccountFactory.User;
// Now you can reference `$scope.user.EmailAddress`
// and it will be kept in sync with `AccountFactory.User.EmailAddress`
});
It should be pretty straight forward. Either you bind the instance of the service or just the email property to the $scope.
Here I'm just updating the email after 5 secs.
myApp.factory('myService', function($http, $timeout) {
return {
email: 'foo#bar.com',
updateEmail: function() {
var self = this;
$timeout(function() {
$http.get('/echo/json/').success(function() {
self.email = 'bar#foo.com';
});
}, 5000);
}
};
});
1st Method:
Bind the entire service on the scope as:
function MyCtrl($scope, myService) {
$scope.myService = myService;
myService.updateEmail();
});
<div ng-controller="MyCtrl">
myService: {{myService.email}}!
</div>
2nd Method
Just create a custom $watch for email updates:
function MyCtrl($scope, myService) {
$scope.email = myService.email;
myService.updateEmail();
$scope.$watch(function() { return myService.email; }, function(newVal, oldVal) {
$scope.email = newVal;
});
}
<div ng-controller="MyCtrl">
$scope: {{email}}
</div>
I would recommend the first method because it requires only one $watch to update the DOM i.e. for {{myService.email}} whereas the second method requires two $watches i.e. one to update the $scoped model ($scope.$watch) and other to update the DOM as {{email}}.
Demo: http://jsfiddle.net/HB7LU/3015/
Related
Which is the right way to declare a service ?
.service('myService', function(myFactory) {
myFactory.LoadData("...",function(newData){
this.data= newData;
});
}
or
.service('myService', function() {
var data= {};
myFactory.LoadData("...",function(newData){
data= newData;
return data ;
});
}
I don't want to make http calls everytime I use it but only once, and then access the data whenever I want.
I've tried the first example but i'm getting 'undefined' value when I try to use it in my controller.
EDIT:
ok here is my code:
the service :
.service('ClassService', function(ClassFactory) {
ClassFactory.query(function(rawClasses){
this.classes= [];
rawClasses.forEach(function(classe) {
var classeRow={};
classeRow.grade=classe.grade;
classe.branch.forEach(function(branch) {
classeRow._id=branch._id;
classeRow.branch= branch.name;
classeRow.fees= branch.fees;
branch.sub_class.forEach(function(subClass) {
classeRow.label=subClass.label;
classeRow.name= _getGradeName_(classeRow.grade)+(classeRow.branch || '')+ (subClass.label || '');
console.info('ClasseService hihihi!');
this.classes.push(_byValue_(classeRow));
console.log( this.classes);
}, this);
}, this);
}, this);
});
})
and the controller:
.controller('StudentController', function(StudentFactory, ClassService, $scope, $stateParams) {
//...
$scope.classes= ClassService.classes;
//but here console.log($scope.classes) gives 'undefined'
//...
});
The query in your service is running asynchronously so you need to use a promise to wait for the value to become available.
If you make ClassFactory.query() return a promise instead of using a callback it will all become much simpler. Use thePromise.then() in ClassService to handle the completion, but that also returns a promise which you should expose to the controller. Then in the controller use another .then() to know when the data is available.
So assuming you've updated ClassFactory.query() to return a promise:
.service('ClassService', function(ClassFactory) {
var promise =ClassFactory.query();
this.dataLoaded = promise.then(... the code to convert rawClasses to this.classes goes here...);
})
and:
.controller('StudentController', function(StudentFactory, ClassService, $scope, $stateParams) {
//...
ClassService.dataLoaded.then(function () {
$scope.classes= ClassService.classes;
});
});
I have a controller that will have identical functionality to another by manage different data. I am new to angularJS so I'm not exactly sure how to proceed. I've read about services and factories but have only seen examples of injecting the same data across different controllers instead of different data to the same controller. Any help to point me in the right direction is appreciated.
angular.module("myApp")
.controller("AirlineController", function () {
this.Airlines = getAirlines(); //some service call that will be ajax eventually
});
angular.module("myApp")
.controller("CitiesController", function () {
this.Cities = getCities();//some service call that will be ajax eventually
});
angular.module("myApp")
.controller("GenericController", function () {
$('.selected-items-box').bind('click', function (e) {
e.stopPropagation();
$('.image-select-wrapper .list').toggle('slideDown');
});
$(document).bind('click', function () {
$('.image-select-wrapper .list').slideUp();
});
this.ListObject = getAirlines();//this list should be populated from one of the other controllers
this.toggleSelected = function (selectedItem) {
angular.forEach(this.ListObject, function (ListItem) {
ListItem == selectedItem ? ListItem.selected = true : ListItem.selected = false;
});
};
this.getSelectedItem = function (item) {
return item.selected;
};
});
Use function parameters to making a factory more versatile.
app.factory("getGeneric", function($http) {
var apiUrl = "http:/my.com/api/"
//Use function parameter
return function (arg1) {
//return promise
return $http.get(apiUrl + arg1);
}
});
Then in your controllers.
app.controller("AirlineController", function (getGeneric) {
var vm = this;
//use function parameter
var airlinesPromise = getGeneric("Airlines"); //service returns promise
airlinesPromise.then( function onFulfilled(response) {
vm.Airlines = response.data;
});
});
app.controller("CitiesController", function (getGeneric) {
var vm = this;
//use function parameter
var citiesPromise = getGeneric("Cities"); //service returns promise
citiesPromise.then( function onFulfilled(response) {
vm.Cities = response.data;
});
});
Please notice that most servive APIs are asynchronous and do not return data immediately. The AngularJS $http service returns promises and data needs to be extracted from the promise with its .then method.
Another point is make factories generic and make controllers lean and specific. Controllers should be lean and specific to their HTML.
You can certainly achieve that. You can have a factory/service that has the methods with parameters that you can pass from the controller. For example I have two controllers and one service that both the controllers are calling.
Based on the the parameter values passed, the service will return different set of data. I'm using the $scope but you can use this but the idea remains the same.
angular.module('SelectOptionModule')
.controller('AirlineController', function ($scope, AirlineService) {
$scope.Airline = AirlineService.GetAirLines("a")
});
angular.module('SelectOptionModule')
.controller('Airline2Controller', function ($scope, AirlineService) {
$scope.Airline = AirlineService.GetAirLines("b")
});
angular.module('SelectOptionModule')
.factory('AirlineService', AirlineService);
function AirlineService() {
function GetAirLines(value) {
if (value == "a")
{
return [{ "Id" : "1", "Name" : "AA" } ]
}
if (value == "b") {
return [{ "Id": "2", "Name": "Delta" }]
}
}
return {
GetAirLines: GetAirLines
};
}
The View can be like to test this out.
<div ng-app='SelectOptionModule' >
<div ng-controller="AirlineController">
{{ Airline }}
</div>
<div ng-controller="Airline2Controller">
{{ Airline }}
</div>
</div>
I'm having trouble setting $rootScope for Angularjs.
Below is my function
App.controller('Controller',
function (UtilityService, $rootScope) {
var setSession = function () {
$rootScope.test = "yes"; // <-- I get this
UtilityService.getSession().success(
function () {
$rootScope.test = "No"; // <-- I don't get this How do I get/set this value?
});
};
setSession();
});
Additional Info:
One of the ways that might work is to set up a service that is interacted between multiple controllers. Does anybody know how to do this with the service returning an http.get json object.
I'm having trouble getting a dynamic scope in my controller that is instantiated within a service.
In order to address my issue I had to
1) Pass $rootScope into my 2nd controller
App.controller($rootScope) {
2) Set my 2nd controller's function to $rootScope
$rootScope.functionCall = function () {};
3) Set my passed value to $rootScope ($rootScope.orderId)
$rootScope.functionCall = function () {
Service.getItems($rootScope.orderId).success(
function(results) {
$scope.items = results;
});
};
4) within my utility controller, I loop through my results, parsing them, and setting them to $rootScope as you can see in #3 I am initializing "$rootScope.orderId"
angular.forEach(results, function (value, key) {
if (key != null) {
$parse(key).assign($rootScope, value);
}
});
5) I am re-calling the controller's function from within my service call! This is what did the magic for me putting my variable "in scope"
$rootScope.functionCall();
6) I am also testing to see if the function exist cause different pages utilize the utility code but may not have the function to execute
if (typeof $rootScope.functionCall == 'function')
var setSession = function () {
UtilityService.getSession().success(
function (results) {
// Place the rootscope sessions in scope
angular.forEach(results, function (value, key) {
if (key != null) {
$parse(key).assign($rootScope, value);
}
});
// Have to bypass "scope" issues and thus have to execute these fns()
if (typeof $rootScope.functionCall == 'function') {
$rootScope.functionCall();
}
});
};
setSession();
As I wrote before I would use $scope when possible and if you need to share data across multiple controllers you can use a service. The code should be something like:
var app = angular.module('myApp', []);
app.factory('$http', 'myService', function ($http, myService) {
var customers = {};
$http.get("http://www.w3schools.com/website/Customers_JSON.php")
.success(function (response) {
customers = response;
});
return {
customers: customers
};
});
app.controller('controller_one', function($scope, myService) {
$scope.serv = myService.customers;
});
app.controller('controller_two', function($scope, myService) {
$scope.serv = myService.customers;
});
Newbie here!)
I have this service:
angular.module('autotestApp').service('GroupPageService', function () {
var group = "";
this.list = function () {
return group;
};
this.update = function (new_group) {
group = new_group;
};
});
and this controller:
angular.module('autotestApp').controller('GroupPageController', function ($scope, $http, $routeParams, GroupService, $modal, GroupPageService) {
$scope.groupId = $routeParams.id;
$scope.group = GroupPageService.list();
var getGroup = function (id) {
$http({
method: "get",
url: "/enterprises/_groups/"+id
}).success(function (response) {
GroupPageService.update(response.group);
}).error(function () {
console.log("Error while fetching data of one particular group")
});
};
getGroup($scope.groupId);
}
My logic is:
getGroup() function fetches data from web API and updates the variable "group" in the service
$scope.group is assigned to data that is returned by service.list() function
Data is being returned from the web API correctly but there is a problem with the rest.
The variable $scope.group is not being updated
How to fix this?
You can watch for a service method using $watch.
$scope.$watch(function() {
return GroupPageService.list();
}, function(newValue, oldValue) {
if (newValue !== oldValue) {
$scope.group = GroupPageService.list();
}
}, true);
You need to assign the data that you get back from the API in your success method. The first assignment you do for $scope.group at the top only get executed when the controller runs for the first time. There is nothing updating the contents of $scope.group afterwards.
Regarding services: You generally use services when you want to share data across your app. In your case if you want to retrieve those groups once and then inject your service into multiple controllers and have that data available.
It seems that when you assign a new value in your service you are changing the reference to that value. What you should do for your code to work is transform your group variable into an object:
app.service('GroupPageService', function () {
var group = {name: "xxx"} ;
this.list = function () {
return group;
};
this.update = function (new_group) {
group.name = new_group;
};
});
I have a service that fetches some client data from my server:
app.factory('clientDataService', function ($http) {
var clientDataObject = {};
var cdsService = {
fetch: function (cid) {
//$http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('/clients/stats/' + cid + '/').then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response);
// The return value gets picked up by the then in the controller.
clientDataObject = {'data': response.data, 'currentClientID': cid};
return clientDataObject;
});
// Return the promise to the controller
return promise;
}
};
return cdsService;
});
Then in one controller I do:
//get stats
clientDataService.fetch($scope.id).then(function (response) {
$scope.client_data = {
'statistics': response.data
}
});
Which all works very well. However, I'm trying to do a watch from another controller on that service to update it's scope when the data changes, rather then having to re-kick off the http request:
$scope.$watch('clientDataService.clientDataObject', function (cid) {
alert(cid);
});
I'm just alerting for now, but it never ever triggers. When the page initially loads, it alerts "undefined". I have no errors in the console and all the $injects are fine, but it never seems to recognize that data has changed in the service. Am I doing something wrong in the watch?
Many thanks
Ben
clientDataService.clientDataObject is not part of your controller's scope, so you can't watch for changes on that object.
You need to inject the $rootScope into your service then broadcast the changes to the controllers scopes.
app.factory('clientDataService', function ($rootScope, $http) {
var clientDataObject = {};
var cdsService = {
fetch: function (cid) {
var promise = $http.get('/clients/stats/' + cid + '/').then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response);
// The return value gets picked up by the then in the controller.
clientDataObject = {'data': response.data, 'currentClientID': cid};
$rootScope.$broadcast('UPDATE_CLIENT_DATA', clientDataObject);
return clientDataObject;
});
// Return the promise to the controller
return promise;
}
};
return cdsService;
});
Then in the controller you can listen for the change using:
$scope.$on('UPDATE_CLIENT_DATA', function ( event, clientDataObject ) { });
Another approach can be:
define new service
app.factory('DataSharingObject', function(){
return {};
}
include this new service in controller where we want to store the data
app.factory('clientDataService', function ($http, DataSharingObject) {
DataSharingObject.sharedata = ..assign it here
}
include this new service in controller where we want to access the data
app.factory('clientReceivingService', function ($http, DataSharingObject) {
..use it here... = DataSharingObject.sharedata
}