Link service based on $http to controller - angularjs

In my controller I am using a service based on $http which on success is updating the $scope.
Also, I want to have the access to the service in my directive, the on ng-click in directive make some POST, and then refresh the data from server and update the scope afterwards. So the route for the signal would be: directive -> service -> controller -> $scope. How can I do that without invoking a method on controller, but invoking it from directive?
I know I can bind some methods between directive and controller, but I have like 8 possible methods and I don't want to write 8 times method1:"&" and <myDirective method1="method1()" method2="method2() "etc... That would be a mess.
I don't want you to write much code for me, just some hints, please...

If you do not create isolated scope, or set scope=true inside your directive, the directive runs within the scope of controller.
So your options then are to either
Have the methods on the controller and access them as normal scope methods in the directive.
Inject the service into directive as you do it in controller and call the service and update the controller scope however you want
Still if you want to make directive reusable, it is better to use isolated scopes.

Related

Injecting service directly into template

Is it possible to access a service directly from an expression in a template, such as inside an ngClick? I didn't think it was possible, but Angular material's Menu demo seems to inject a custom service $mdOpenMenu as easily as injecting $event into the expression: https://material.angularjs.org/latest/demo/menu
As you can see in the source for the menu controller, the menu directive causes $mdOpenMenu to be assigned to the scope of the markup within that directive, so $mdOpenMenu is available within the markup and can be passed to functions. It isn't a service in the traditional Angular sense. It is just a function that the directive's controller added to the scope.

AngularJS $scope related to 'as myController'

I have a MainController and a PageController. The MainController manages data and gets a $scope,$http as a parameter. I use it like ng-controller="MainController"
I use PageController to manage the tabs on the website, so it has like this.setTab and this.isTabSelected and I use it like ng-controller="PageController as pager".
if I use the alias 'as XXY' I can't seem to access $scope, can I only use $scope WHILE using as and referring to as pager.setTab?
My Goal is to $emit a notification when the page changes, but I can not use $scope and not refer pager, because pager is the global controller (root scope basically) so it is like this:
... ng-controller="PageController as pager" ...
.......ng-controller="MainController" .........
Any clearance on $scope in combination with 'as XXY' and this. Thanks
The controllerAs syntax is the preferred syntax for AngularJS. When you use this syntax you do not bind controllers to templates using the $scope directly. Instead use instance methods and properties of the controller.
The $scope is still used within the controller to bind and emit events, watchers, and call $apply().

How can I update a directive when a service finishes loading in a controller in AngularJS

In an AngularJS app, I have a service that uses $http and returns a promise. I have a controller which requires the service. In the controller DataService.then(function(data){ scope.viewModel.data = data;})
Then I have a directive with isolate scope.
scope: { data: '='} then do something with scope.data inside the directive.
Finally the html
<div ng-controller="myCtrl">
<div my-cool-directive="" data="viewModel.data"></div>
</div>
My question is this presents a chicken before the egg scenario. When the service data is hard coded, all is well and glorious, however when using async $http to actually call a server and get data, scope.data is null inside the directive. How can I properly get the directive the data it needs when the service call finishes and the controller scope property is set. I do not want the directive to have a dependency on the service. I prefer that the controller will drive the directive model. Is $emit or $broadcast the way to go and use a $watch? Basically eventing? or is there a preferred way to do this? I'm certain others have faced this exact issue. I would like the directive to continue to have isolate scope as I may want to extend at some point. I did try to Google first but I don't think I was phrasing the question correctly.
<div my-cool-directive="" data="viewModel.data" ng-if="viewModel.data"></div>
should do the trick.
You could also create a watch in the directive, so that the callback function of the watch is called when the data changes from undefined to something else.

How to output data from a directive to a controller, and should I?

Use case
For use in a form, I created a directive that tracks changes in an array. It allows changes to be reverted and deletions and additions to be stored separately. It allows for an array (one to many mapping in the database) to be updated incrementally (rather than requiring the server to either diff, or rewrite the entire list).
Problem?
My question is about the way I expose the functionality to the controller's scope. I currently use an two-way databound attribute on the directive's scope. This works, and it seems reliable (of course you can easily break it by reassigning the scope's value, but intentionally you can break anything).
Code
You can see this plunk to see this in action. It allows methods on the directive's controller to be called from the view and the view's controller. (I am using the directive controller intentionally because that's what I do in my actual code for the directive to directive communication, but this could also just be placed in the linking function.)
Question
Is this way of doing it bad design? Am I completely throwing AngularJS out of the window now and hacking in my own code. Are there any better ways to expose functions from a directive (keep in mind that there'll be multiple of these in a single form).
It's very easy to pass in my-attribute="someFunction()" to have the directive be a consumer of the view controller. I can't find a better way to do the opposite and have the view controller consume from the directive.
Alternative?
I've been thinking about using a service here, in which the service will provide an object that is instanciated in the view, passed to the directive, and have the directive blurp out it's results to that object. Then in turn have the view controller consume the information from that service's object. Would this be a better approach?
There's nothing wrong with your approach. In fact built-in angular directives such as ng-form use this approach to store the controller in the scope (see the name property of ng-form) http://docs.angularjs.org/api/ng.directive:ngForm
For more re-usability though I would put the api methods on the controller and then put the controller itself in the api:
this.getChanges = function () {};
this.resetChanges = function(){};
$scope.api = this;
In directives, the main purpose of the controller is to serve as an api for other directives (if you didn't need an api for other directives you could just do everything in the link function). Doing it this way ensures the api is available both on the scope as well as to any directive that 'requires' the oneToMany directive.

Is it ok to use $controller inside a controller?

I don't want to repeat the same code that I have in one of my controllers, so I have two options:
use $controller service inside other controller to have some kind of inheritance
use multiple ng-controller on the same element - but I don't know if this is possible?
What is the best solution to implement some kind of controllers inheritance ?
To quote Angular Docs: Controller Inheritance Example:
Controller inheritance in Angular is based on Scope inheritance.
In terms of the example in the docs, you could put your common methods on the $scope inside ChildCtrl, then those methods will automatically be available in the $scope inside BabyCtrl.
Although personally, I prefer adding common code into a service and injecting that service into multiple controllers.
There is no problem in mixing controllers. You can't use ng-controller twice in the same element though, so you would need to inject $controller or create a directive that binds to the controller.
It doesn't make much sense to inject $controller inside the main controller of the element, instead, create a directive that is bound to the componentized controller, and this way, you will have componentized a behavior. Please refer to this answer When to write a directive? to understand what I say.
Controllers are meant to control the $scope, and you don't have a way to extend the controllers. There is no problem in have multiple controllers changing a single scope and using it as a medium of communication between them.
Only use a service if you have a repetitive operation that you do not need a scope necessarily, just like fetching data, manipulating objects, calculating and etc.
You could also consider isolating the common functionality into a service.

Resources