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

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.

Related

Method polymorphism / inheritance in AngularJS 1.6

I am facing problem with controllers overriding in AngularJS.
Here comes the problem:
function UiCoreController($scope, ...) {
'use strict';
angular.extend(vm, {
validatePremium: validatePremium
}
function validatePremium() {
console.log('validate premium in core controller');
// validation logic
vm.calculatePremium();
}
function calculatePremium() { ... }
}
function UiAskController($rootScope, $scope, $controller) {
'use strict';
var vm = this;
var core = $controller('UiCoreController', { $scope: $scope });
angular.extend(this, core);
angular.extend(vm, {
...
}
function calculatePremium() { ... }
}
When I call validatePremium() method from template (by ng-change directive) - it is called from UiCoreController, but calculatePremium() is not called by more descriptive UiAskController::calculatePremium().
That works only if copy-paste whole validatePremium() method to UiAskController but it seems to me like code duplication.
Would anyone help me achieving the calls to calculatePremium() in Java-like code?
Javascript is not OOP like Java and therefore there is no polymorphism and inheritance. So short answer is no. You cannot achieve what you want by using controllers only. The function calculatePremium is bind to controller scope and for sure you don't want to share scopes between controllers.
You can write this method to $rootScope but its not good practice
I would put vm.calculatePremium() logic into service. Its a right place to make this kind of stuff.
Keep in mind that main purpose of controllers is to bind your data to DOM a.e. render view by using scope.
Since Service is a singleton, all data manipulations you can do in services to avoid code duplicate and make code maintenance easy

AngularJS : pass data from controller to controller without factory, service or broadcast?

I don't want to use a service or a factory and would like to pass for example an array of data. I would like to access data in my parent controller from my child component.
Factory and service are excluded since i eventually want to migrate my app to angular 2, and i don't want to use ngclick which seems inseperable with broadcast/up/on.
If anyone knows how to pass data on the background (without user interaction, like input or ngclick) using broadcasting, it would work aswell :)
What are my options ?
Thank you !
If you have nested components, you can access the parent's data with require
var child = {
bindings: {},
require: {
parent: '^^parent'
},
controller: childController,
templateUrl: 'child.template.html'
};
Now in your child controller you have access to an instance of the parent controller and thus can call methods and access it's properties:
this.parent.parentMethod();
You have some more detailed code in a previous answer here:
Where should I place code to be used across components/controllers for an AngularJS app?
Your other choices:
bindings
Just like directives' scope or bindToController you can bind data and methods through html attributes using the bindings propety of your component
<component-x shared="$ctrl.shared"></component-x>
var componentX = {
bindings: { shared: '=' }
...
$rootScope
Never use it to store data. It works but it's not made for that purpose and will lead to unmaintainable code.
Services
It's a common misconception that shared data should be done through services.
It was true and good practice before 1.5 though.
Controller inheritance
Another bad practice (imo).
In a classic MVC app nested controllers can inherit parents with the $controller service:
.controller('MainController', function ($scope) {
$scope.logMessage = function(message) {
console.log("Message: " + message);
}
})
.controller('ServicesController', function($scope, $controller) {
$controller('MainController', {$scope: $scope});
});
Broadcast and emit Events
It's the way to go if the event you're broadcasting makes sense application wide (login, logout...etc.) If you're updating a variable in a component, don't use it.
I don't want to use a service or a factory and would like to pass for
example an array of data
You can use localStorage/sessionStorage to store and fetch the data

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)

AngularJS DI for custom functions

I've been working a lot lately with AngularJS and I really like the dependency injection stuff. I'm using it all over the place, but just in Angular components, like controllers and the like.
This always calls on the angular object:
angular.module('app').controller(/**/);
Now I have this function:
var custom = function(MyService) {
// do stuff
};
I've declared the Service this way:
angular.module('app').factory('MyService', function($rootScope) {
return {
show: function() {
// do stuff
};
},
hide: function() {
// do stuff
}
};
});
I now want to use this service in my custom function. Is there a way of manually calling the angular DI container? (I couldn't find anything in the docs...)
I know that this works for controllers not defined with the angular.module()... thing:
function Controller(MyService) {
MyService.hide(); // works
}
But how to use it outside of AngularJS components, in completely independent functions?
Or do I have to take a completely different path to achieve my goal?
Cheers and thanks in advanced,
Christian
angularjs is pretty neat in the fact that it exposes its own internal services to the user. The service you're looking for is $injector
What you want to use to call your custom function is $injector.invoke(myCustomFunction,thisForCustomFunction,{named:locals});
If you are by chance wanting to invoke this function outside of angular you'll have to get the applications injector.
var injector = angular.element(elementWithNgApp).injector();
Note:
The invocation time may be reduced though. As the injector must annotate (find what services you need) using several regular expressions. The reason this is not an issue throughout angular is because it is done once. Because services,factories,providers, are all instantiated (newed) singletons that provide closure with the services and whatever uses the services inside your providers
To prevent this extra step you can provide a $inject property on your function.. Something like this:
myfunction.$inject = ['ServiceA','ServiceB'];
function myfunction(a,b){
//a is ServiceA
//b is ServiceB
};

Angular JS - How to safely retain global value "extracted" using a service

I need an object to be globally accessible all throughout my Angular application, and I've gladly put this object in a value service.
Unfortunately, this object is computed by another service, and I've not been able to inject myService into same value service.
ATM, I've acheived my goal with something like this:
global service code
app.service('myService', function() {
this.foo = function() {
// some code that returns an object
return computedObject
}
})
app.run(function ($rootScope, myService) {
$rootScope.global = {dbObject: myService.foo()}
})
And which I can use in any controller that pleases me by simply injecting $rootScope in it
However, I don't like the need of injecting the whole $rootScope wherever I need that damn object, and I trust is not much safe (or efficient?) either, since the team specifies
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
Conversely, don't create a service whose only purpose in life is to store and return bits of data.
Do you, perchance, happens to know any way I can inject a service into a service value?
Or maybe any other Angular best practice which I could exploit?
I forgot one very important notice
The computation of the object could be quite computational intense, so I absolutely don't want it to be recomputed everytime I move from page to page, or anything else really.
Ideally Using $rootScope for storing a few globally required values is totally ok. But if you are still adamant on using a module, I suggest you use a provider.
Make your 'myService' a provider and that will let you configure the variables in the service.
More info here
AngularJS: Service vs provider vs factory
You could use $broadcast to send the value from the value service to myService.
You would still need to inject $rootscope into each of the services, but from then on you could just inject myService around the rest of the app.
Reference here.
I need an object to be globally accessible all throughout my Angular application
I would use service. Since Service is singleton you can register the service to all your controllers and share any data over service.
Unfortunately, this object is computed by another service, and I've not been able to inject myService into same value service.
Just create one main service (Parent) and child service that will inherit the parent. (like abstract service in Java world).
Application.factory('AbstractObject', [function () {
var AbstractObject = Class.extend({
virtualMethod: function() {
alert("Hello world");
},
abstractMethod: function() { // You may omit abstract definitions, but they make your interface more readable
throw new Error("Pure abstract call");
}
});
return AbstractObject; // You return class definition instead of it's instance
}]);
Application.factory('DerivedObject', ['AbstractObject', function (AbstractObject) {
var DerivedObject = AbstractObject.extend({
virtualMethod: function() { // Shows two alerts: `Hey!` and `Hello world`
alert("Hey!");
this._super();
},
abstractMethod: function() {
alert("Now I'm not abstract");
}
});
return DerivedObject;
}]);
Now, we can add some logic into AbstractObject and continue use DerivedObject
Here is example Plunker

Categories

Resources