Communicate between two same directive having isolated scope - angularjs

I am novice to angularjs and i have a custom directive which has an isolated scope. I am having problem communicating between two instance of same directive .
How can i do that ? Your suggestion is highly appreciated .
<div date-control="cal1" ng-model="mydate" calendar-properties="calendarProperties1"></div>
<div date-control="cal2" ng-model="mydate2" calendar-properties="calendarProperties2"></div>
What i want to do is open both date-control at once having 'to' and 'from' attribute in calendar properties using some data sharing or any thing ? you can check this plnkr sample.

directives are just the result of a function call
angular.directive('myDir',function(){
var common;
return directiveObject;
});
directiveObject being the any of the variantes you use to create your directive.The main thing is that right before returning you can declare common(class if you will) variables and methods that can be checked for changes or invoked so you could do something like
app.directive('myDir',function(){
var bus={
value1:0
};
function increase(){
bus.value1++;
}
return {
scope:{},
template:"<div><h1>{{counter}}</h1><button ng-click="increase()">add</button></div>"
controller:function($scope){
$scope.bus=bus;
$scope.increase = increase
$scope.$watch('bus',function(){
//something here
})
}
};
});
a sample of this can be found here. this can be shaped in many ways
http://plnkr.co/edit/d9dpIYCAjOaOBNjoI80u?p=preview
some other methods can be used like emitting and broadcasting events or even using services, but i like the simplicity of this method.

You can either store this information directly in a Service, or you can have the directives communicate with each other. In this case, I'd have them communicate.
One option that would work is to use .$broadcast to send a message from the $rootScope and then use .$on to receive that message.
Example: inside your "From" directive
// Once "From" date is selected
$rootScope.$broadcast('from:set', date);
Then, inside your "To" directive
scope.$on('from:set', function (event, data) {
console.log(data); // Will log "From" date
});
There are a lot of different ways you can do this that will work (e.g. you could put them both on the $rootScope or you could have them .$emit upward instead of .$broadcast downward), so use whatever you feel makes the most sense in your case.
There is a great blog post on the subject here: http://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/

You are basically trying to share data across different $scope. You will get many suggestions, about storing in $rootScope, $broadcast, but these solutions are not optimal as they do the job but with an overhead.
Create a service, and store the properties "to" and "from" into that service, then inject that service into your directive..
http://plnkr.co/edit/xJrBneiU8RU92czHAZ43?p=preview
.service("MyService", function(){
var from = 'Cal1';
var to = 'Cal1';
return {from:from, to:to};
})
.controller('DatepickerDemoCtrl', function ($scope, MyService) {
$scope.calendarProperties1 = {
format: "yyyy-MM-dd",
label: "Enter the Date From",
from: MyService.from,
opened:false
}
$scope.calendarProperties2 = {
format: "yyyy/MM/dd",
label: "Enter the Date To",
to: MyService.to,
opened: false
}
})
In this example, you will be able to see how we can use Service variables inside directives ... Using this solution will solve your problem
**Edit, I have edited your plunker code, to modify the calendar properties object to use data from the service. Now both calendar properties object will get data from the same service variable.. I hope this might solve your problem

I want to thank you all for answering and providing some suggestion.If i want to communicate through one directive with another directive of same type i will retrieve the scope of the directive to call or share/get the data of another directive by using .scope() method . There may be other way but i am just a novice of angular and i am using this way to access the methods and data between directives .

Related

AngularJS - Sharing data from a parent route controller to all child directives on the page

I have a page that has multiple components and I've created each component as a directive. When the page is first loaded, that's when I grab all the data that should be available on the page. So all of the data exists on the controller for that route, which we'll just call pageCtrl. And then what I've been doing is binding any required data to each directive through the attributes, which of course ends up creating an isolate scope for each of them.
I know there are a few ways to share data, so given this situation, is there a recommended way of doing it? Or has anyone had better success doing it one particular way? While it's working perfectly fine the way I'm doing it, I've run into a few caveats. If I need just even one bit of information from the pageCtrl, I need to add another attribute to the directive. So it ends up creating more code on the directive element itself.
I was thinking about just creating a service that would store all the data, which the pageCtrl could initialize, instead of setting it on itself. Any feedback would be appreciated.
good question :)
First solution is to create in parent controller object and pass this object (via ng-model) to all directives. This object will be passed by reference (not by value) so controller and all directives will have access to the same object.
```
// in controller
$scope.shared_data = {someItems: []};
// in html
<my-directive ng-model=shared_data></my-directive>
Second solution is to create some simple service to store all of those data.
// in this solution you have to inject additional service to directive controller
(extended idea of point 2) creating service/factory that will be responsible by collecting and returning data. This service could be injected into directive and use the same methods to collect data. To avoid making multiple calls to API (REST) it could have some cache for each sensitive method.
Communication via events.... (probably the worsts solution for your example)
The first two ideas are probably the best, I do not know full specification of your product so final solution picking belongs to You:).
My advice is to try/play with all of those methods to really understand what is going on and how and when to use each of them :)
You can directly call your parent controller from child directive controller by using $parent.
App.controller('aCtrl', ['$scope', function ($scope) {
$scope.refresh=function(){
.......... //Updated Data get from DB
};
...........
}]);
App.directive('bDirective',function(){
restrict: 'EC',
replace: true,
scope: {},
controller: function($scope) {
$scope.$parent.refresh();
}
...
});
HTML:
<div ng-controller="aCtrl">
<div class="bDirective"></div> //directive
</div>

Recommended way to handle AngularJS Directive data

I regularly see examples such as <calendar events="a.appointments"></calendar> in which data assigned within a controller is passed in to a directive via an attribute binding.
The code below shows an alternative solution, in which the required data is gathered directly within the directive link function. Using this approach eliminates the need for a separate controller.
diary.html
<calendar></calendar>
calendar.js
angular.module('diary').
directive('calendar', ['AppointmentsService', function(AppointmentsService) {
return {
template: 'calendar.html',
scope: {},
link: function($scope) {
$scope.events = {};
AppointmentsService.getAppointments().then(function(result) {
$scope.events = result;
});
}
};
}]);
Is this a suitable or ultimately flawed approach to take?
If your calendar directive is completely decoupled from everything, it is absolutely fine approach to go for. However, if you need a certain communication between calendar and, let's say, events-tracker directive, you might want to enclose them in a parent controller. Or if you have a collections of calendar directives on a single page (with an option to remove or add new calendar), then passing the data by reference is a way to go as well.
EDIT
e.appointments are probably passed to calendar through scope, not attribute, i.e. calendar having isolated scope with events field.
Having thought about this some more, I've realised the alternative solution does have a flaw and it's a reusability issue.
When providing events through an attribute binding to the directive's isolate scope, the source of the data is totally flexible.
Fetching the appointments directly within the link function of the directive however, creates a specific dependency on AppointmentsService as the data source.

Splitting up the directive's controller into smaller parts

So I've made this Plunker that works well as a demo: http://plnkr.co/edit/Zm9d6zHhrnqDlnJsSZ1I?p=preview . It's a simple pagination with two attribute arguments that holds the model-state and some config-state. I want to end up with a directive factory like this (or something explaining how to reason differently):
angular.module('mnPagination').factory(function(model, config) {
return {
model: model,
config: config
}
})
My issue with the current plunker is that the app layer and the directive layer doesn't look alike. Since I only have one app I can use factories as singleton data providers. That's really good!
But inside the directive, I can't use any kind of provider since it will be a singleton. The scope is a new object for every declared instance so that's the only place I can put any stateful code that should be contained in the directive.
Are directives supposed to be stateless?
Another more meta question: Am I the only one freaking out about this?
It's my second SO attempt, and noone on facebook or at work are really that into MVVM/MVC or directives with isolated scope.
I'm Leya, come be my Luke!
So the way I solved this was by creating a factory inside an angular factory.
angular.module('mnPagination').factory('mnPaginationFactory', function() {
var factory = function (items, config) {
...
}
return factory
}
Now I get a new object for each directive by calling the factory from the controller which has the items and config objects on the scope.
Plunker here: http://plnkr.co/edit/DPTZUjeMihsva5nJ3IVx?p=preview .

Exposing directive controller to parent controller

I want to expose some of my directive's functionality through its controller (think a public API for this directive).
return {
restrict: 'E',
scope: {},
controller: function($scope) {
this.method1 = ...;
this.method2 = ...;
},
controllerAs: 'dir',
link: function (scope, element, attrs) { ... }
}
Then in my parent controller or template call dir.method1 to get stuff accomplished inside the directive. Any ideas if this is possible as of Angular 1.3?
I'd like to refrain from event passing or even function passing, I have heard this is possible although I have never seen an implementation of this.
It is possible, but your issue isn't to figure out how to get the API out. It's how to get TO it from the parent. You're creating an isolate scope through your use of the 'scope' option. You're also making an element-type directive, so I'm guessing you're doing something like this:
<my-parent>
<my-child></my-child>
</my-parent>
where <my-parent> is the parent directive, and <my-child> is the directive with the API you want to expose.
The real question is what you're trying to achieve here. There is totally a way to do what you're asking. Just because the scope is isolated doesn't mean you can't get to it. You can just iterate through the parent $scope's $$childHead/etc list to find the child whose API you want to access. Anything you define in the child like this:
$scope.myApiFunction = function() {
};
will be visible here. (Things you put into 'this' will not - use the $scope storage bucket instead.)
That means if you only had ONE child you could do something like this from the parent controller:
$scope.$$childHead.myApiFunction();
Simple. Also, very crude. There are lots of problems here: what if you have many children? What if this child with its API ends up one level down? Etc. It's breaking all kinds of OO patterns and it's going to get messy, fast.
Your question is very abstract - it might be good if you updated it with an exact example. Without that, let me guess at your goal. There are two ways to do something "like this" that are encouraged within Angular:
Services. Whenever you say "API", think Service first. A service is a singleton (automatically) so it's tailor-made for creating APIs. And services can use the Factory pattern to return objects of a type, so THOSE are tailor made for doing things like having a manager service handle, say, a buddy list in an IM client, with API methods for creating, removing, and finding buddies.
Items that add "optional" functionality to their parents when they're defined. Let's say we have three possible types of tooltips: tooltips that have a hover effect, those that have a click effect, and those that are triggered by a "walkthrough" system in some order. For this kind of thing, the easy thing to do is just reverse the API, like this:
Parent Controller:
$scope.tooltipHandler = {
showTooltip: function() {},
hideTooltip: function() {}
};
Child Controller:
$scope.$parent.tooltipHandler = {
showTooltip: function() {
// Do some real work
},
hideTooltip: function() {
// Do some real work
},
}
What happens here is if there's no tooltip defined, when the parent runs its walkthrough, nothing happens. If you add the blue tooltip display module, when the parent runs its walkthrough now, it's going to show blue tooltips.
Make sense?
I arrived here looking for a similar response. So far the best that I can figure is to do what Angular does with ngForm.
In the documentation clearly states
If the name attribute is specified, the form controller is published onto the current scope under this name.
This basically makes the form controller accessible from anywhere.
If you have the following DOM
<div ng-controller="MyCtrl as parentCtrl">
<form name="parentCtrl.frmCtrl">
<my-child-directive>
</form>
</div>
You can use require: 'ngForm' in my-child-directive to get access from an inside directive. If you are in the parent controller you can access it trough the frmCtrl variable.
Not sure if this is best practice. In ngForm the name attribute works well, but I don't even know how to call such an attribute for a custom directive.
Thats why I arrived here, I wanted to know if this is "The Angular way" and what types of convetions are on the subject.
Hope it helps!

Is passing data between directives, controllers, and services using $emit and $broadcast bad practice?

Hi I'm a beginning Angular developer and I was wondering if the way I've passed data between controllers is bad practice or not.
I'm creating a seatmap widget whereby you click on a seat and it will display data in another part of the App using a different controller.
1) I have a directive that incorporates dynamic templating. Based on the model being passed (from an ng-repeat), it will create an event listener like so:
if(scope.seat.isAvailable) {
element.bind('click', function() {
scope.$emit('clickedSeat', scope.seat);
}
element.append(seatAvailableTemplate);
}
2) In my controller, I have the following listening for the click:
$scope.$on('clickedSeat', function(event, seat) {
seatSelectionService.broadcastSeatData(seat);
}
3) I have a service where I've passed in $rootScope which allows me to broadcast the data to a different controller:
var seatSelectionService = {};
seatSelectionService.broadcastSeatData = function(clickedSeat) {
seatSelectionService.seatData = clickedSeat;
$rootScope.$broadcast('seatSelected');
}
return seatSelectionService;
4) In the other controller, I have a listener which sets the $scope attribute, which renders {{selectedSeat}} in the view:
$scope.$on('seatSelected', function() {
$scope.selectedSeat = seatSelectionService.seatData;
$scope.apply();
}
Of course I have had to pass in seatSelectionService into both controllers for it to work.
Is there a better way to do this? Or is this way of passing data valid practice?
I would say less is more- if you can get rid of code do it- I would suggest you remove this in your controller unless you are doing something you are not showing, and put it in your directive, in step one:
$scope.$on('clickedSeat', function(event, seat) {
seatSelectionService.broadcastSeatData(seat);
}
$broadcast and $emit themselves aren't bad practice, but it does look like you've over complicated your app.
You can add services directly to directives - they can have controllers built into them that depend on the services.
So in your directive, add a controller that uses the seatSelectionService, something like:
seatSelectionService.setSeat( scope.seat )
Which cuts out one of the steps at least - your setSeat method would still want to do a $broadcast when it's done down to anything listening.
Directives can be really simple or really complex - they can simply be a quick way of outputting a repeated template, or a dynamic template with a controller and access to multiple services.
Something else you might want to look at is promises - I used to use $broadcast and $emit a lot more than I needed to, until I learnt how to use promises and defer properly

Resources