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>
Related
I've been struggling with this for a few days now and can't seem to find a solution.
I have a simple listing in my view, fetched from MongoDB and I want it to refresh whenever I call the delete or update function.
Although it seems simple that I should be able to call a previously declared function within the same scope, it just doesn't work.
I tried setting the getDispositivos on a third service, but then the Injection gets all messed up. Declaring the function simply as var function () {...} but it doesn't work as well.
Any help is appreciated.
Here's my code:
var myApp = angular.module('appDispositivos', []);
/* My service */
myApp.service('dispositivosService',
['$http',
function($http) {
//...
this.getDispositivos = function(response) {
$http.get('http://localhost:3000/dispositivos').then(response);
}
//...
}
]
);
myApp.controller('dispositivoController',
['$scope', 'dispositivosService',
function($scope, dispositivosService) {
//This fetches data from Mongo...
$scope.getDispositivos = function () {
dispositivosService.getDispositivos(function(response) {
$scope.dispositivos = response.data;
});
};
//... and on page load it fills in the list
$scope.getDispositivos();
$scope.addDispositivo = function() {
dispositivosService.addDispositivo($scope.dispositivo);
$scope.getDispositivos(); //it should reload the view here...
$scope.dispositivo = '';
};
$scope.removeDispositivo = function (id) {
dispositivosService.removerDispositivo(id);
$scope.getDispositivos(); //... here
};
$scope.editDispositivo = function (id) {
dispositivosService.editDispositivo(id);
$scope.getDispositivos(); //... and here.
};
}
]
);
On service
this.getDispositivos = function(response) {
return $http.get('http://localhost:3000/dispositivos');
}
on controller
$scope.addDispositivo = function() {
dispositivosService.addDispositivo($scope.dispositivo).then(function(){
$scope.getDispositivos(); //it should reload the view here...
$scope.dispositivo = '';
});
};
None of the solutions worked. Later on I found that the GET request does execute, asynchronously however. This means that it loads the data into $scope before the POST request has finished, thus not including the just-included new data.
The solution is to synchronize the tasks (somewhat like in multithread programming), using the $q module, and to work with deferred objects and promises. So, on my service
.factory('dispositivosService',
['$http', '$q',
function($http, $q) {
return {
getDispositivos: function (id) {
getDef = $q.defer();
$http.get('http://myUrlAddress'+id)
.success(function(response){
getDef.resolve(response);
})
.error(function () {
getDef.reject('Failed GET request');
});
return getDef.promise;
}
}
}
}
])
On my controller:
$scope.addDispositivo = function() {
dispositivosService.addDispositivo($scope.dispositivo)
.then(function(){
dispositivosService.getDispositivos()
.then(function(dispositivos){
$scope.dispositivos = dispositivos;
$scope.dispositivo = '';
})
});
};
Being my 'response' object a $q.defer type object, then I can tell Angular that the response is asynchronous, and .then(---).then(---); logic completes the tasks, as the asynchronous requests finish.
UPDATE
I am currently doing this, and I'm not sure why it works, but I don't think this is the correct approach. I might be abusing digest cycles, whatever those are.
First, I want to have the array navigationList be inside a service so I can pass it anywhere. That service will also update that array.
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigationList = [];
var getNavigation = function() {
ExtService.getUrl('navigation.json').then(function(data) {
angular.copy(data.navigationList, navigationList);
});
}
return{
getNavigation: getNavigation,
navigationList: navigationList,
}
}]);
Then in my controller, I call the service to update the array, then I point the scope variable to it.
ChapterService.getNavigation();
$scope.navigationList = ChapterService.navigationList;
console.log($scope.navigationList);
But this is where it gets weird. console.log returns an empty array [], BUT I have an ng-repeat in my html that uses $scope.navigationList, and it's correctly displaying the items in that array... I think this has something to do with digest cycles, but I'm not sure. Could anyone explain it to me and tell me if I'm approaching this the wrong way?
I have a main factory that runs functions and calculations. I am trying to run
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData : function () {
ExtService.getUrl('navigation.json').then(function(data) {
return data;
});
}
}
return: {
navigation: navigation
}
I did a console.log on the data before it gets returned and it's the correct data, but for some reason, I can't return it..
The ExtService that has the getUrl method is just the one that's typically used (it returns a promise)
In my controller, I want to do something like
$scope.navigation = ChapterService.navigation.getNavigationData();
in order to load the data from the file when the app initializes,
but that doesn't work and when I run
console.log(ChapterService.navigation.getNavigationData());
I get null, but I don't know why. Should I use app.run() for something like this? I need this data to be loaded before anything else is done and I don't think I'm using the best approach...
EDIT
I'm not sure if I should do something similar to what's being done in this jsfiddle, the pattern is unfamiliar to me, so I'm not sure how to re purpose it for my needs
My code for ExtService is
app.factory('ExtService', function($http, $q, $compile) {
return {
getUrl: function(url) {
var newurl = url + "?nocache=" + (new Date()).getTime();
var deferred = $q.defer();
$http.get(newurl, {cache: false})
.success(function (data) {
deferred.resolve(data);
})
.error(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
});
EDIT 2
I'd like to separate the request logic away from the controller, but at the same time, have it done when the app starts. I'd like the service function to just return the data, so I don't have to do further .then or .success on it...
You are using promises incorrectly. Think about what this means:
var navigation = {
getNavigationData : function () {
ExtService.getUrl('navigation.json').then(function(data) {
return data;
});
}
}
getNavigationData is a function that doesn't return anything. When you're in the "then" clause, you're in a different function so return data only returns from the inner function. In fact, .then(function(data) { return data; }) is a no-op.
The important thing to understand about promises is that once you're in the promise paradigm, it's difficult to get out of it - your best bet is to stay inside it.
So first, return a promise from your function:
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
return ExtService.getUrl('navigation.json');
}
}
return {
navigation: navigation
}
}])
Then use that promise in your controller:
app.controller('MyController', function($scope, ExtService) {
ExtService
.navigation
.getNavigationData()
.then(function(data) {
$scope.navigation = data;
});
})
Update
If you really want to avoid the promise paradigm, try the following, although I recommend thoroughly understanding the implications of this approach before doing so. The object you return from the service isn't immediately populated but once the call returns, Angular will complete a digest cycle and any scope bindings will be refreshed.
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
// create an object to return from the function
var returnData = { };
// set the properties of the object when the call has returned
ExtService.getUrl('navigation.json')
.then(function(x) { returnData.nav = x });
// return the object - NB at this point in the function,
// the .nav property has not been set
return returnData;
}
}
return {
navigation: navigation
}
}])
app.controller('MyController', function($scope, ExtService) {
// assign $scope.navigation to the object returned
// from the function - NB at this point the .nav property
// has not been set, your bindings will need to refer to
// $scope.navigation.nav
$scope.navigation = ExtService
.navigation
.getNavigationData();
})
You are using a promise, so return that promise and use the resolve (.then) in the controller:
app.factory('ChapterService', [ 'ExtService', function(ExtService) {
var navigation = {
getNavigationData: function () {
return ExtService.getUrl('navigation.json'); // returns a promise
});
}
return: {
navigation: navigation
}
}
controller:
ChapterService
.navigation
.getNavigationData()
.then(function (data) {
// success
$scope.navigation = data;
}, function (response) {
// fail
});
This is a different approach, I don't know what your data looks like so I am not able to test it for you.
Controller
.controller('homeCtrl', function($scope, $routeParams, ChapterService) {
ChapterService.getNavigationData();
})
Factory
.factory('ChapterService', [ 'ExtService', function(ExtService) {
function makeRequest(response) {
return ExtService.getUrl('navigation.json')
}
function parseResponse(response) {
retries = 0;
if (!response) {
return false;
}
return response.data;
}
var navigation = {
getNavigationData: function () {
return makeRequest()
.then(parseResponse)
.catch(function(err){
console.log(err);
});
}
}
return navigation;
}])
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;
});
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/
I meet a problem with $resource of angularjs
I have a service factory :
angular.module('publicApp').factory('Model', function ($resource) {
var totalStock = $resource('./resource/data/totalStock',{},{}});
return {
totalStock : totalStock
}
});
And a controller get data from service :
angular.module('publicApp')
.controller('ManageCtrl', function ($scope, Model) {
$scope.total = Model.totalStock.query();
});
then, in another controller, when a function inside it success, I recall the services :
angular.module('publicApp')
.controller('ActionCtrl', function ($scope, Model) {
$scope.checkout = function () {
$http.post('/resource/action/sold', formData).success(function (resp) {
if (resp.resCode == -1) {
alert(resp.result);
return;
}
console.log('sold!');
Model.totalStock.query();
});
}
});
My idea that when Model.totalStock.query() service recall, the $scope.total value of ManageCtrl is updated also. Anyone suggest me do it in right way?
I am not sure, but this may work:
You can create two factories:
'ModelRessource' factory is only used by your 'Model' factory.
angular.module('publicApp').factory('ModelRessource', function ($resource) {
var totalStock = $resource('./resource/data/totalStock',{},{}});
return {
totalStock : totalStock
}
});
Then, you use 'Model' factory to interact with your ressource. If you bind your $scope to Model.totalStack, every time refreshTotalStack() is call, totalStack should be updated.
angular.module('publicApp').factory('Model', function (ModelRessource) {
var data = {};
data.totalStack = {};
return {
totalStack : data.totalStack,
refreshTotalStack : function () {
ModelRessource.totalStock.query(function (resp) {
data.totalStack = resp;
return data.totalStack;
});
}
}
});