AngularJS feature available for multiple controllers
Building a simple dashboard in AngularJS API + Rest .
Built a simple factory that makes an API request (GET, POST)
and returns the required data for a successful callback. The return data must be processed and modified $scope
, as the API can return server side field errors.
I am unable to build handling / change $scope
in the factory since the factory does not (and should not) have access to the scope. I would rather not handle / apply in the success callback as it will be repetitive (once per API request).
What is the best "Angular" way to solve this problem?
One possible solution is for the function to exist outside of the Angular application and then simply pass the scope and data to it.
This looks like a bad job (see below).
myApp.controller("saveForm", ["$scope", "api", function($scope, api), function() {
...
$scope.submit = function() {
api("POST", url, $scope.data, function(data) {
//onSuccess
processData($scope, data);
});
}
...
}]);
myApp.factory('api', ['$http', function($http) {
return function(method, url, input, success, error) {
//Retrieve data from API. Note that factory DOES NOT have access to $scope.
$http(...
}
}]);
var processData = function(scope, data) {
angular.forEach(data, function(value, key)) {
scope....
}
}
source to share
Not sure if I got you, but you can extend controllers in a mixin way:
Base controller
(function() {
'use strict';
angular.module('Base', []);
function BaseController($scope, <injectables>, that) {
that.mixin1 = function() {
};
that.mixin2 = function(arg1, arg2) {
};
}
angular.module('Base').controller('BaseController',
['$scope', '...', BaseController]);
})();
Legacy controller
(function() {
'use strict';
angular.module('Derived', ['Base']);
function DerivedController($scope, $controller, ...) {
$controller('BaseController', {
'$scope' : $scope,
...
'that' : this
});
// this.mixin1
// this.mixin2
}
angular.module('Derived').controller('DerivedController',
['$scope', '$controller', '...', DerivedController]);
})();
Please note that you are using Angular service to bundle $controller
functions.
source to share
Why not just include a function in the controller that handles it? This is how I usually deal with it:
myApp.controller("saveForm", ["$scope", "api", function($scope, api), function() {
...
$scope.submit = function() {
api("POST", url, $scope.data, function(data) {
//onSuccess
processData(data);
});
}
function provessData(data){
// do stuff to data
$scope.foo = data;
}
...
}]);
source to share
It seems like your controller is doing too much work and knows too much about the actual request (url, "POST" method, etc.).
How do I convert data in a factory to what the controller expects. factory doesn't need to know anything about scope. It simply converts the data into a format that the controller can use and then sends back. This way the factory can be reused across controllers.
myApp.controller("saveForm", ["$scope", "api", function($scope, api), function() {
...
$scope.submit = function() {
// Call API.update and handle the deferred that is returned, which will be the transformed data
api.update($scope.data).then(function (transformedData) {
scope....
});
}
...
}]);
myApp.factory('api', ['$http', '$q', function($http, $q) {
return {
update : function () {
var deferred = $q.defer();
$http({
method: "GET",
url: "your/url/path"
}).success(function(data) {
var transformedData;
// Perform data transformation here instead of in controllers
angular.forEach(data, function (value, key) {
transformedData.....
});
deferred.resolve(transformedData);
});
return deferred.promise;
}
}
}]);
source to share
You can create a factory that returns your function processData
:
myApp.factory('processData', [function () {
return function(scope, data) {
angular.forEach(data, function(value, key)) {
scope....
}
};
}]);
And then inject it angular:
myApp.controller("saveForm", ["$scope", "api", "processData", function($scope, api, processData) {
...
$scope.submit = function() {
api("POST", url, $scope.data, function(data) {
//onSuccess
processData($scope, data);
});
}
...
}]);
The advantage of this approach in regards to a function declared outside of DI is that it is still easy to mock a dependency processData
in your unit tests if you need to.
source to share