Directive to directive communication using a service - angularjs

I am attempting to perform the following. Use one directive to set information in a service.
Use another directive to retrieve information from this service. The directive setting the information seems to be doing it's job fine, however the one receiving the information does not react to it.
Below are how the directives look:
app.service('theStore',function(){
this.data;
});
app.directive('theOneThatSets', ['theStore', function(theStore){
return {
restrict: 'E',
link: function(scope, element, attrs) {
element.click(function(event){
theStore.data = attrs.val;
});
}
};
}]);
app.directive('theOneThatReads', ['theStore', function(theStore){
return {
restrict: 'E',
template: '<stong>Received Text is - {{receivedValue}}</strong>',
link: function(scope, element, attrs) {
scope.$watch('theStore.data',function(newVal){
scope.receivedValue = theStore.data;
});
}
};
}]);
plnkr here: http://plnkr.co/edit/9EMIwhUcneQoopNqqWtV

I don't know if you can do watchers on things that are not in scope. The best way to communicate between controllers/services/directives is to use $rootScope, $broadcast, and $on.
Example using your code:
app.directive('theOneThatSets', ['$rootScope', function(theStore){
return {
restrict: 'E',
link: function(scope, element, attrs) {
element.click(function(event){
//theStore.data = attrs.val;
$rootScope.$broadcast('changeThisValue', attrs.val); // Send
});
}
};
}]);
app.directive('theOneThatReads', [function(theStore){
return {
restrict: 'E',
template: '<stong>Received Text is - {{receivedValue}}</strong>',
link: function(scope, element, attrs) {
scope.$on('changeThisValue', function($event, value){
scope.receivedValue = theStore.data;
});
}
};
}]);
also, try creating a listener in your service like so:
app.service('myservice',function(){
this.listen = function($scope) {
$scope.$watch(function(){return someScopeValue},function(){
//$scope.dosomestuff();
});
}
});
//your controller
function myCtrl($scope,myservice) {
$scope.listen = function() {
myservice.listen($scope);
}
//call your method
$scope.listen();
}

Related

Is there any ways to update directive link with scope or some other values?

I have dC directive
app.directive('dC', function($rootScope) {
return {
restrict: 'AE',
link: function(scope, element, attrs) {},
templateUrl: function(elem, attrs) {
return $rootScope.somePosiT
}
}
});
And I want to access inside of my controller as function is there anyway can I achieve it in order to change my directive location dynamically when click fired
app.controller('appCtrl', function($scope, $rootScope) {
clk: function() {
$rootScope.somePosiT = 'views/1.html'
}
})
I think you could do it using template and ng-include - rather than templateUrl. I think templateUrl is called before the scope values are available. You could try something like this:
app.directive('dC', function($rootScope) {
return {
restrict: 'AE',
link: function(scope, element, attrs) {},
template: '<div ng-include="somePosiT"></div>'
};
});
app.controller('appCtrl', function($scope, $rootScope) {
$scope.clk = {
somePosiT: "path/to/template.html"
};
});

How to append a custom angular directive into another custom angular directive?

I have two custom angular directives and one appends the second repeatedly. The problem is that although the tag is appended, the template of the directive is not. When I manually put it in, it works.
See this jsfiddle: http://jsfiddle.net/HB7LU/5555/
Here is the code where the appending takes place:
myApp.directive('formList', function () {
return {
template: '<my-form></my-form>',
require:'^repeatableForm',
restrict: 'E',
link: function (scope, element, attrs, repeatableFormCtrl) {
scope.add = function () {
console.log("test");
element.append('appended <my-form></my-form>'); // apended<my-form></my-form> will appear but not the contents of <my-form>
};
}
};
});
You have to use $compile service to manually compile your my-form directive like this:
myApp.directive('formList', function ($compile) {
return {
template: '<my-form></my-form>',
require:'^repeatableForm',
restrict: 'E',
link: function (scope, element, attrs, repeatableFormCtrl) {
scope.add = function () {
console.log("test");
var newForm = $compile('<span>appended </span><my-form></my-form>')(scope);
element.append(newForm);
};
}
};
});
Example JSFiddle: http://jsfiddle.net/9L3whcqc/

Can an angularjs Directive require Itself?

Can a directive require itself? Here's an example:
app.directive('menu', function () {
return {
restrict: 'E',
require: '?^menu',
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});
In my test it doesn't seem to work (ctrl is always undefined). See the plunk
BTW, after this question was answered I discovered that in this case the caret (^) has no effect and the controller passed to the link function is always the instance's own controller. [ plunk ]
You should directly define controller function to expose directive API to other directives:
app.directive('menu', function () {
return {
restrict: 'E',
require: '?^menu',
controller: function($scope){ },
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});
See http://plnkr.co/edit/cKFuS1lET56VOOYD5rrd?p=preview
With angular 1.4x, you actually can now limit the require statement to parent elements only and exclude the element itself. If you change
require: '?^menu' to require: '?^^menu' (notice the second caret) so that you get
app.directive('menu', function () {
return {
restrict: 'E',
require: '?^^menu',
controller: function($scope){ },
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});
the code now works as expected.
See http://plnkr.co/edit/2uDUO0LcgDX7xEuBtsJ2?p=preview
I guess here the problem is not with the directive referencing itself. The directive will not know which controller to refer to until specified or defined. To access a controller either it has to be defined or referenced in the directive as below.
app.directive('menu', function () {
return {
restrict: 'E',
controller: 'MainCtrl',
require: ['?^menu'],
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl[0]);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});

How can I get my directive to access the controllers scope

I have a setup like this:
<controller>
<directive>
in my controller that has a function that returns an html string. How can I get my directive to render this by accessing the controllers scope?
Or maybe I should just put the controller in the directive?
app.controller('controller', ['$scope', 'DataService', function ($scope, DataService) {
$scope.parseJson = function () {
//returns the html
};
}]);
directive
app.directive('Output', function () {
return {
restrict: 'A',
replace: true,
template: '<need html from controller>',
link: function(scope, element, attr) {
//render
//scope.parseJson();
}
};
});
You should use the isolated scope: '&' option
app.directive('output', ['$sce', function ($sce) {
return {
restrict: 'A',
replace: true,
template: "<div ng-bind-html='parsed'></div>",
scope:{
output: "&"
},
link: function(scope){
scope.parsed = $sce.trustAsHtml(scope.output());
}
};
}]);
Template:
<div output="parseJson()"></div>
The directive and the controller should be sharing the scope already. Don't bother using a template for the directive, just get the HTML string in you linking function (you already have the method call in there) and modify the element directly using element.html(). Take a look at the element docs for more info.
app.directive('Output', function ($compile) {
return {
restrict: 'A',
link: function(scope, element, attr) {
var templateString = scope.parseJson();
var compiledTemplate = $compile(templateString)(scope);
compiledTemplate.appendTo("TheElementYouWishtoAppendYourDirectiveTo");
}
};
});

Angular.js trigger another directive

I'm trying to create a sort of generic toggle feature between directives, where one directive which contains a template does not render, until an event occurs from another directive. Any suggestions on how to link this together?
Thanks!
There are many ways to achieve this.
A
Using events (but be careful, when used exessively, especially for interaction between directives, you can get lost easily! This is why I didnt create a http://plnkr.co for it, even worse: code A is untested!
so pls edit this in case of errors
Use $rootScope.$on('myEvent', function(e, eargs) {...}) on the
master directive.
dispatch the event from some directive:
$rootScope.$broadcast('myEvent', {foo: 'bar'}).
remember to inject $rootScope in both directives.
angular.module('masterDirective', [])
.directive('masterDirective', function ($rootScope, $compile /**injects here*/) {
var templ = '<p ng-bind="someVar"></p>';
return {
restrict: 'EA',
scope: {},
link: function (scope, element, attrs) {
scope.someVar = "I am a template and I was born and visible to the world, because slaveDirective send me an event to do so.";
$rootScope.$on('myEvent', function(e, eArgs) {
// eArgs.myVar will be 'Jackson';
element.append($compile(templ)(scope));
});
}
}
});
angular.module('slaveDirective', [])
.directive('slaveDirective', function ($rootScope) {
return {
restrict: 'EA',
scope: {},
link: function (scope, element, attrs) {
$rootScope.$broadcast('myEvent', {myArg: 'Jackson'});
}
}
});
B
Using a "shared controller" is the cleaner, but more complicated way. This approach is more strongly typed, you express the workflow and once it works, it is not as easy to break.
Demo: http://plnkr.co/WaqKzP
Use a controller on your master directive: controller(scope,element,attrs) {...}
require your masterDirective in slave directive: require: 'myMasterDirective'
the controller of the master directive is the fourth parameter of your slave's link function (because you required it), you can call a function to let the master include the template.
<body ng-app="myApp">
<button ng-click="includeSlave=true">include slave directive</button>
<master-directive>
<div ng-if="includeSlave==true">
<slave-directive></slave-directive>
</div>
</master-directive>
</body>
angular.module('myApp', [])
.directive('masterDirective', function ($rootScope, $compile /**injects here*/) {
var templ = '<p ng-bind="someVar"></p>';
return {
restrict: 'E',
controller: function ($scope, $element) {
return {
slaveLink: function() {
$element.append($compile(templ)($scope));
}
}
},
link: function (scope, element, attrs) {
scope.someVar = "I am a template and I was born and visible to the world, because slaveDirective called a function on myself to do so.";
}
};
})
.directive('slaveDirective', function () {
return {
require: '^masterDirective',
restrict: 'E',
link: function (scope, element, attrs, myMasterController) {
myMasterController.slaveLink();
}
};
});

Resources