Angular.js Call $http.get from outside controller - javascript

I have an HTTP resource that returns a JSON list of top 10 entities from a database.
I call it this way:
var filter= "john";
var myApp = angular.module('myApp', []);
myApp.controller('SearchController', ['$scope','$http', function ($scope, $http) {
$http.get('/api/Entity/Find/' + filter). //Get entities filtered
success(function (data, status, headers, config) {
$scope.entities = data;
}).
error(function () {
});
}]);
It works!
But... how can I change the filter variable in order to change the query?
Should I rewrite the whole controller to get this to work?
Update
Sorry for the lack of clarity in my question. When I asked this I couldn't undertand anything of AngularJS.
My original intent was to get the variable $http injected, without relying on creating a controller for that.
Thanks for everyone.

A likely better method
If you don't want to get it inside a controller, you could have it injected into a recipe (ex, provider, factory, service):
https://docs.angularjs.org/guide/providers
myApp.factory('getStuff', ['filter', '$http', function (filter, $http) {
//Blah
}]);
If you want to get an instance of $http outside of any angular struct, you can do what's shown below.
The method given by Dennis works; however, it does not work if called before angular has been bootstrapped. Also, it seems like Derek has an error with Dennis' method because he does not have jquery.
The solution that Exlord mentioned is better, as it does not have that problem, and is more proper:
$http = angular.injector(["ng"]).get("$http");
Explained:
The angular injector is an:
object that can be used for retrieving services as well as for dependency injection
https://docs.angularjs.org/api/ng/function/angular.injector
The function angular.injector takes the modules as a parameter and returns an instance of the injector.
So in this case you retrieve an injector for the ng module (angular's), and then retrieve the service $http.
Note:
One thing to keep in mind when using injector like this is that in my own findings it seems you need to make sure you include modules in the inject which what you are "getting" will need. For example:
angular.injector(['ng', 'ngCordova']).get('$cordovaFileTransfer')

Regarding to your question "... call $http.get from outside controller" you can do the following:
... ANYWHERE IN YOUR CODE
var $http = angular.element('html').injector().get('$http');
$http.get(...).success(...);
ANYWHERE IN YOUR CODE ...
See official docs from angular: angular $injector docs :
The get(name); method Returns an instance of the service.

Related

Pass data to different controllers using Angularjs

I have the below code in one controller.
$scope.DataModel= [];
$scope.DataModelTexts= { buttonDefaultText: '' };
I will be using the same code in one more controller. Now instead of writing the same code in 2nd controller too, i want to know if there is a way to put this in a common code in some factory or service and use that in both the controllers. I have tried to read about factory and services and i am getting a bit confused of how to use any one of these in my scenario.
Any information would help me gain more knowledge about factory and services in angularjs. Thanks.
You're on the right track, use can use a factory or a service to share code between controllers. Note that in angular services(and factories) are singletons; they are instantiated once when the app starts and then anytime you inject it into a controller, you are referencing the same instance. Consider the following code:
var myApp = angular.module('myApp',[]);
myApp.service('MyService', function() {
let _someValue = 'Initial Value';
this.setValue = function(value){
_someValue = value;
}
this.getValue = function(){
return _someValue;
}
});
//First Controller Run
myApp.controller('ControllerA', function($scope, MyService) {
MyService.getValue(); //Initial Value
MyService.setValue("BRAND NEW VALUE!!!");
});
//Run after ControllerA
myApp.controller('ControllerB', function($scope, MyService) {
MyService.getValue(); //BRAND NEW VALUE!!!
});
Her you'll see that MyService holds the state of someValue. ControllerA get MyService injected to it and can use the methods of that service to set a new value. Now for any subsequent call for that same state, like for instance by ControllerB, the updated value will be returned.
You can use the .config() or a run() blocks (good SO on these here: AngularJS app.run() documentation?) to bind these reused variables to $rootScope, then call them from $rootScope.DataModel and $rootScope.DataModelTexts from within your controllers or services (as long as you inject $rootScope into these controllers and services).

Where do I put regular functions, Angularjs

I'm getting into Angularjs. I'm want to re-use a function, resetForm() function. My question is, do I still put that inside my controller $scope or create a factory or service?
app.controller('testController', [
'$scope',
'testService',
function($scope, testService) {
$scope.addTestForm = function() {
var body = document.getElementsByTagName('body')[0];
if (!body.classList.contains('test__add')) {
body.classList.add('test__add');
}
};
//do I add my function here?
function name() {};
}]);
if it is a resetForm() function then I assume it is dealing with DOM. I would suggest you to declare this function inside your controller since you will need access to $scope to reset form fields (direct DOM access is strictly prohibited in AngularJS). You can refer to below sample code
app.controller('testController', [
'$scope',
'testService',
function($scope, testService) {
var resetForm = function() {
// your logic to reset form with help of $scope
};
$scope.addTestForm = function() {
var body = document.getElementsByTagName('body')[0];
if (!body.classList.contains('test__add')) {
body.classList.add('test__add');
}
};
}]);
Note: You don't need to declare resetForm function as $scope.resetForm if you don't plan to call it from your template file.
If you want to re-use it across multiple controllers, a Factory or a Service is probably the best way to share it without duplication of code. You can then call on either one of these from all your controllers.
The added benefits to this pattern are that, not only do you save yourself from duplicating code, but you can also store variables and share those as well.
Both will work, but you can read some interesting discussion on Factory vs Service if you have trouble with which one to choose.
The things goes like this:
We will write functions in controllers if that function is normally manipulating model and is only relevant to that controller.
We write services normally for giving data to controllers such as from a asynchronous API call, and for sharing data in between controllers.
In your case, if you want a utility function you can use a service, but resetForm function is more like controller specific, because it's gonna clear some model values. In future you may want to add more conditions and operations in that function which may produce complex code, if you use a service for that.
If that function is a 'non-gonna change function' using a service is good way to go. (code re-usability and all), but otherwise, wrap all logic in one place is more good.
(write it in controller)

How does implicit/inline/$inject dependency injection work in AngularJS?

I'm new to AngularJS and I would like to understand more about the dependencies that are being injected by default. While reading through code I've noticed that sometimes dependencies are explicitly declared beforehand, and sometimes they aren't. For example:
someModule.controller('MyController', ['$scope', 'someService', function($scope, someService) {
// ...
}]);
Gives the same results as:
someModule.controller('MyController', function($scope, someService) {
// ...
});
How does this work? Is Angular assuming that the modules being injected are named the same as the variables in the parameters?
Also, strangely enough, if you do specify the dependencies that are going to be injected, you must specify all of them and in the right order, otherwise nothing will work. For example, this is broken code:
someModule.controller('MyController', ['someService', '$scope', function($scope, someService) {
// Won't give us any errors, but also won't load the dependencies properly
}]);
Can someone clarify to me how is this whole process working? Thank you very much!!
Yes, dependency injection in Angular works via the names of the components you (and Angular - for the internal ones) registered.
Below is an example showing how a service is registered and injected into a controller using several different annotations. Please note that dependency injection always works the same in Angular, i.e. it doesn't matter if you are injecting something into a controller, a directive or a service.
app.service('myService', function () {
// registering a component - in this case a service
// the name is 'myService' and is used to inject this
// service into other components
});
Two use (inject) this component in other components, there are three different annotations I am aware of:
1. Implicit Annotation
You can either specify a constructor function which takes as parameters all the dependencies. And yes, the names need to be the same as when these components were registered:
app.controller('MyController', function ($http, myService) {
// ..
});
2. Inline Array Annotation
Or you can use a notation using an array, where the last parameter is the constructor function with all the injectables (variable names do not matter in this case). The other values in the array need to be strings that match the names of the injectables. Angular can this way detect the order of the injectables and do so appropriately.
app.controller('MyController', ['$http', 'myService', function ($h, m) {
/* Now here you can use all properties of $http by name of $h & myService by m */
// Example
$h.x="Putting some value"; // $h will be $http for angular app
}]);
3. $inject Property Annotation
A third option is to specify the $inject-property on the constructor function:
function MyController($http, myService) {
// ..
}
MyController.$inject = ['$http', 'myService'];
app.controller('MyController', MyController);
The reason why the last two options are available, at least as far as I know, is due to issues which occured when minifying the JavaScript files which led to the names of the parameters being renamed. Angular then wasn't able to detect what to inject anymore. In the second two cases the injectables are defined as strings, which are not touched during minification.
I would recommend to use version 2 or 3, as version 1 won't work with minification/obfuscation. I prefer version 3 as from my point of view it is the most explicit.
You can find some more detailed information in the internet, e.g. on the Angular Developer Guide.
Just to provide a different sort of answer, as to the how inline/implicit dependencies work in AngularJS. Angular does a toString on the provided function and parses the parameter names from the string which is produced. Example:
function foo(bar) {}
foo.toString() === "function foo(bar) {}"
References:
source code
AngularJS Dependency Injection - Demystified

AngularJS controller needs to have data from $http before initialized

I have a problem to initialize controller in AngularJS.
Below is the process which I want to implement.
Get data from mongoDB by $http before DOM is ready.
By Using the data, some div element should be created using ng-repeat.
But the problem is that the view is rendered before controller gets data from $http.
So I searched all over the stack-overflow and google, and found about ui-router's resolve function.
Below is my ui-router code.
.state('floor', {
url: '/floor/:domainId',
templateUrl: 'modules/floor/views/floor.client.view.html',
controller: 'FloorController',
resolve: {
initData: ['$http', '$stateParams', function($http, $stateParams) {
return $http.get('/users/getFloor/' + $stateParams.domainId).success(function(user) {
return $http.get('/users/' + user._id + '/data/get').success(function(data) {
return data;
});
});
}]
}
})
The first $http is to get user id from domain id. (e.g. User can connect to /floor/my_personal_domain_address), and the second $http is what I need for initial data.
This is my Controller code.
angular.module('floor').controller('FloorController', ['$scope', 'initData',
function($scope, initData) {
console.log(initData);
}]);
Small tip or search keyword or anything will be very thankful for me.
I'm still learning AngularJS so just give me a small tip please.. Thank you!
UPDATE
This was my misunderstanding of how controller works. As some people commented, I didn't have to use resolve to retrieve data before controller initialized. The problem was occurred because I declared array variable used in ng-repeat as [] for the first time and client shows error. If I declare the variable after I get value from database, controller data-bind it to view properly.
So the problem is solved. Thank you all for valuable comments and answers.
UPDATE 2
Anyway, ui-router's resolve can return a value even though it is promise. I worked for it for some hours, and what I found out is that if I return $http promise in resolve, controller can get its data when successful. But if $http error is occurred in resolve, nothing can catch it. So if there's someone who wants to use resolve to send data to controller before it is initialized, I think it must be used with care.
Get data from mongoDB by $http before DOM is ready.
In this case probably the simpler solution would be not to make any tricky $http requests before Angular initialization but instead just to embed your data as JavaScript global variable into the main HMTL page just before loading of angular.min.js script.
I don't know if I get your question correctly, but this should help you:
(from the ui-router docs https://github.com/angular-ui/ui-router/wiki)
// Another promise example. If you need to do some
// processing of the result, use .then, and your
// promise is chained in for free. This is another
// typical use case of resolve.
promiseObj2: function($http){
return $http({method: 'GET', url: '/someUrl'})
.then (function (data) {
return doSomeStuffFirst(data);
});
},
So you'd have to use .then() instead of .success() and it should work.

Is it good practise to use angular.element("[ng-controller="someCtrl"]").scope()

Is it good practise to use angular.element("ng-controller="someCtrl"]").scope() instead of using factory to handle data flow between controllers using dependency injection. The problem here is I want to call a function of another controller, so there are two ways either I put it in a factory and reuse it among controllers or use above syntax to call the function directly.
If you need to call a function from other controller, it should be a SERVICE/Factory. This way, you will share code between controllers, and you will code with good practices.
As they say in angularjs docs
Angular services are substitutable objects that are wired together
using dependency injection (DI). You can use services to organize and
share code across your app.
Then, you just need to create a service or a factory
//The service
angular.module('myApp')
.service('serviceName', function ($http, $scope, socket) {
//This functions will be available in your controller
return {
list: function () {
$http.get(listUrl).success(function (lista) {
$scope.centerList = lista;
socket.syncUpdates('center', $scope.centerList);
});
}
};
});
//The controller
angular.module('myApp').controller('myCtrl', function ($scope, centerService) {
$scope.listCenters = function () {
centerService.list();
};
});
Just to clarify, and to add some comprehensive ideas about services and factories:
https://www.youtube.com/watch?v=J6qr6Wx3VPs
AngularJS: Service vs provider vs factory
https://www.airpair.com/angularjs/posts/top-10-mistakes-angularjs-developers-make
It is never good practice to access the DOM from a controller. So if wrapping the method in a factory/service is an option, I'd say that's the way to go.

Categories

Resources