I m actually creating a directive that permit me to generate some graphs, with some parameters at this format :
<div anomalie-graph idsite="{{site._id}}" sector="true" controlleur="" anomalie=""
datedebut="dateDebutStats"
datefin="dateFinStats">
or
<div anomalie-graph idsite="{{site._id}}" sector="" controlleur="" anomalie="true"
datedebut="dateDebutStats"
datefin="dateFinStats">
This directive display correctly what I need.
The problem is that I want to reload the data inside this directive.
In fact, I use something like :
$scope.$on('reload', function () {
reload();
});
Inside of the directive controller.
Now that I have isolated my scope with
return {
restrict: 'EAC',
templateUrl: 'tpl/directive/TraitementAnomalieGraphDirective.html',
controller: controller,
scope: {
idsite: '#',
sector: '#',
controlleur: '#',
anomalie: '#',
datedebut: '#',
datefin: '#'
}
};
I cant listen to the scope event (because it's isolated), and I dont want to use $scope.$broadcast.
Before scope isolation, it worked great with the listener events. The problem was that I couldn't use this library multiple time in a view...
Can you help me ?
EDIT:
Here's my code :
Template:
<div class="row">
<div class="col-md-4">
<div anomalie-graph idsite="{{site._id}}" sector="true" controlleur="" anomalie=""
datedebut="dateDebutStats"
datefin="dateFinStats">
</div>
</div>
<div class="col-md-4">
<div anomalie-graph idsite="{{site._id}}" sector="" controlleur="true" anomalie=""
datedebut="dateDebutStats"
datefin="dateFinStats">
</div>
</div>
</div>
Here's the directive :
angular.module('app')
.directive('anomalieGraph', function () {
var controller = ['$scope', '$attrs', 'srv_traitementsVoirie', function ($scope, $attrs, srv_traitementsVoirie) {
var load = function () {
// code of reload
};
load();
$scope.$on('reload', function () {
load();
});
}];
return {
restrict: 'EAC',
templateUrl: 'tpl/directive/TraitementAnomalieGraphDirective.html',
controller: controller,
scope: {
idsite: '#',
sector: '#',
controlleur: '#',
anomalie: '#',
datedebut: '=',
datefin: '='
}
};
});
Related
I'm creating a directive to show differences in text. For this directive, I need a couple of buttons which I've split up in directives. A simpliefied example would be:
.directive('differenceViewer', function($templateCache, $compile) {
return {
restrict: 'E',
scope: {
oldtext: '#',
newtext: '#',
template: '#',
heading: '#',
options: '=',
itemdata: '&',
method: '&'
},
replace: false,
link: (scope, element, attr) => {
element.append(angular.element($compile($templateCache.get(scope.template))(scope)));
}
};
}).directive('diffbutton', function() {
return {
restrict: 'E',
scope: {
method: '&'
},
template: '<button class="btn btn-sm btn-success" ng-click="method()">Rollback</button>',
replace: true,
terminal: true,
link: (scope, element, attr) => {
scope.directiveClick = function(){
console.log("directive method"); // is never called
}
}
}
})
I compile the html via a template script:
<script type="text/ng-template" id="differenceViewer.html">
<div class="ibox-footer">
<div class="row">
<div class="col-md-12">
<diffbutton method="clickedBtn()">Foo</diffbutton>
</div>
</div>
</div>
</script>
Where the diffbutton is created inside the html compiled by differenceViewer.
I need to call a method in the controller that is responsible for creating all the difference-views.
app.controller('MainCtrl', function($scope) {
$scope.clickedBtn = function() {
console.log('foo'); // is never called
}
})
Here's a plunker demonstrating the issue.
What do I need to change in order to be able to forward the button click on my directive in a directive to the controller method?
I've been working with the answers of this question but still can't make it work.
Note that, if I add
scope.clickedBtn = function() {console.log("bar");}
to the differenceViewer directive, it gets called - however I need to call the method in the controller instead.
Pass on a method from the parent to the child element and then trigger it on click. For example (pseudo code coming in )
<parent-directive>
<child-directive totrigger="parentClickCallback()"></child-directive>
</parent-directive>
then in your parent directive you set
$scope.parentClickCallback = function(){//do whatever you neeed}
and on your child in its scope binding you set
scope:{
totrigger:'&'
}
and on that child button you simply add
<button ng-click="totrigger()">ClickMe</button>
every time you click that button it will trigger parentClickCallback by reference attached to totrigger.
Why would you need to complicate your code so much I'm not really sure. If you not happy with scope binding just require controller in your directive and pass the controller bound function.
I am trying to figure out how to pass a transclusion down through nested directives and bind to data in the inner-most directive. Think of it like a list type control where you bind it to a list of data and the transclusion is the template you want to use to display the data. Here's a basic example bound to just a single value (here's a plunk for it).
html
<body ng-app="myApp" ng-controller="AppCtrl as app">
<outer model="app.data"><div>{{ source.name }}</div></outer>
</body>
javascript
angular.module('myApp', [])
.controller('AppCtrl', [function() {
var ctrl = this;
ctrl.data = { name: "Han Solo" };
ctrl.welcomeMessage = 'Welcome to Angular';
}])
.directive('outer', function(){
return {
restrict: 'E',
transclude: true,
scope: {
model: '='
},
template: '<div class="outer"><inner my-data="model"><div ng-transclude></div></div></div>'
};
})
.directive('inner', function(){
return {
restrict: 'E',
transclude: true,
scope: {
source: '=myData'
},
template :'<div class="inner" my-transclude></div>'
};
})
.directive('myTransclude', function() {
return {
restrict: 'A',
transclude: 'element',
link: function(scope, element, attrs, controller, transclude) {
transclude(scope, function(clone) {
element.after(clone);
})
}
}
});
As you can see, the transcluded bit doesn't appear. Any thoughts?
In this case you don't have to use a custom transclude directive or any trick. The problem I found with your code is that transclude is being compiled to the parent scope by default. So, you can fix that by implementing the compile phase of your directive (this happens before the link phase). The implementation would look like the code below:
app.directive('inner', function () {
return {
restrict: 'E',
transclude: true,
scope: {
source: '=myData'
},
template: '<div class="inner" ng-transclude></div>',
compile: function (tElem, tAttrs, transclude) {
return function (scope, elem, attrs) { // link
transclude(scope, function (clone) {
elem.children('.inner').append(clone);
});
};
}
};
});
By doing this, you are forcing your directive to transclude for its isolated scope.
Thanks to Zach's answer, I found a different way to solve my issue. I've now put the template in a separate file and passed it's url down through the scopes and then inserting it with ng-include. Here's a Plunk of the solution.
html:
<body ng-app="myApp" ng-controller="AppCtrl as app">
<outer model="app.data" row-template-url="template.html"></outer>
</body>
template:
<div>{{ source.name }}</div>
javascript:
angular.module('myApp', [])
.controller('AppCtrl', [function() {
var ctrl = this;
ctrl.data = { name: "Han Solo" };
}])
.directive('outer', function(){
return {
restrict: 'E',
scope: {
model: '=',
rowTemplateUrl: '#'
},
template: '<div class="outer"><inner my-data="model" row-template-url="{{ rowTemplateUrl }}"></inner></div>'
};
})
.directive('inner', function(){
return {
restrict: 'E',
scope: {
source: '=myData',
rowTemplateUrl: '#'
},
template :'<div class="inner" ng-include="rowTemplateUrl"></div>'
};
});
You can pass your transclude all the way down to the third directive, but the problem I see is with the scope override. You want the {{ source.name }} to come from the inner directive, but by the time it compiles this in the first directive:
template: '<div class="outer"><inner my-data="model"><div ng-transclude></div></div></div>'
the {{ source.name }} has already been compiled using the outer's scope. The only way I can see this working the way you want is to manually do it with $compile... but maybe someone smarter than me can think of another way.
Demo Plunker
I have a directive that controls a personalized multiselect. Sometimes from the main controller I'd like to clear all multiselects. I have the multiselect value filling a "filter" bidirectional variable, and I am able to remove content from there, but when doing that I also have to change some styles and other content. In other words: I have to call a method belonging to the directive from a button belonging to the controller. Is that even posible with this data structure?:
(By the way, I found other questions and examples but their directives didn't have their own scope.)
function MultiselectDirective($http, $sce) {
return {
restrict: 'E',
replace: true,
templateUrl: 'temp.html',
scope: {
filter: "=",
name: "#",
url: "#"
},
link: function(scope, element, attrs){
//do stuff
scope.function_i_need_to_call = function(){
//updates directtive template styles
}
}
}
}
The best solution and the angular way - use event.
Live example on jsfiddle.
angular.module('ExampleApp', [])
.controller('ExampleOneController', function($scope) {
$scope.raise = function(val){
$scope.$broadcast('raise.event',val);
};
})
.controller('ExampleTwoController', function($scope) {
$scope.raise = function(val){
$scope.$broadcast('raise.event',val);
};
})
.directive('simple', function() {
return {
restrict: 'A',
scope: {
},
link: function(scope) {
scope.$on('raise.event',function(event,val){
console.log('i`m from '+val);
});
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="ExampleApp">
<div ng-controller="ExampleOneController">
<h3>
ExampleOneController
</h3>
<form name="ExampleForm" id="ExampleForm">
<button ng-click="raise(1)" simple>
Raise 1
</button>
</form>
</div>
<div ng-controller="ExampleTwoController">
<h3>
ExampleTwoController
</h3>
<form name="ExampleForm" id="ExampleForm">
<button ng-click="raise(2)" simple>
Raise 2
</button>
</form>
</div>
</body>
I think better solution to link from controller to directives is this one:
// in directive
return {
scope: {
controller: "=",
},
controller: function($scope){
$scope.mode = $scope.controller.mode;
$scope.controller.function_i_need_to_call = function(){}
$scope.controller.currentState = state;
}
}
// in controller
function testCtrl($scope){
// config directive
$scope.multiselectDirectiveController = {
mode: 'test',
};
// call directive methods
$scope.multiselectDirectiveController.function_i_need_to_call();
// get directive property
$scope.multiselectDirectiveController.currentState;
}
// in template
<Multiselect-directive controller="multiselectDirectiveController"></Multiselect-directive>
I'm trying to use the ui-tinymce directive inside of another directive:
angular.module("risevision.widget.common.font-setting", ["ui.tinymce"])
.directive("fontSetting", ["$templateCache", function ($templateCache) {
return {
restrict: "AE",
template: $templateCache.get("_angular/font-setting/font-setting.html"),
transclude: false,
link: function ($scope, element, attrs) {
$scope.tinymceOptions = {
menubar: false,
statusbar: false
};
}
};
}]);
And _angular/font-setting/font-setting.html:
<div class="row">
<div class="col-md-12">
<textarea ui-tinymce="tinymceOptions" ng-model="tinymceModel"></textarea>
</div>
</div>
The TinyMCE editor shows up, but it's ignoring the configuration options I've set in $scope.tinymceOptions. That is, the menu bar and status bar still show.
Any thoughts as to why it's not working?
Thx.
I know I'm late to the party but I'm going to answer you in case someone has the same issue and can't find the answer.
TinyMce needs to be loaded only when the tinymceOptions variable has data so you need to wrap it in an ng-if:
<div class="row">
<div class="col-md-12" ng-if="tinymceOptions">
<textarea ui-tinymce="$parent.tinymceOptions" ng-model="$parent.tinymceModel"></textarea>
</div>
</div>
You can avoid using $parent (elements inside ng-if have their own scope) using controller as inside the directive if you are using Angular >1.4:
app.directive('someDirective', function () {
return {
scope: {},
bindToController: {
someObject: '=',
...
},
controllerAs: 'ctrl',
controller: function () {
var ctrl = this;
ctrl.message = 'Object loaded.';
},
template: '<div ng-if="ctrl.someObject">{{ctrl.message}}</div>'
};
});
I am having trouble getting a click event in a directive with an isolated scope to work using "controller as" syntax in Angular 1.3 the code for the directive is as follows:
myDirectives.directive('gsphotosquare', dirfxn);
function dirfxn() {
var directive = {
replace: false,
scope: {
photoInfo: '=',
photoBatchNum: '=',
thumbnailwidth: '='
},
restrict: 'EA',
controller: Ctrller,
controllerAs: 'ctrl',
template: '<div ng-click="ctrl.squareClicked()">test</div>',
//templateUrl: 'views/directives/gsphotosquare.html',
bindToController: true, // because the scope is isolated
link: linkFunc //adding this didn't help
};
return directive;
}
function Ctrller() {
var vm = this;
vm.squareClicked = function () {
alert('inside clickhandler for gsphotosquare directive');
};
}
function linkFunc(scope, el, attr, ctrl) {
el.bind('click', function () {
alert('inside clickhandler for gsphotosquare directive');
});
}
And here is how the directive is used in the DOM:
<span class="input-label">General Site Photos</span>
<div class=" item row">
<gsphotosquare photo-info="mt.photos.v1f1[0]" photo-batch-num="mt.photoBatchNum" ></gsphotosquare>
<gsphotosquare photo-info="mt.photos.v1f1[1]" photo-batch-num="mt.photoBatchNum" ></gsphotosquare>
<gsphotosquare photo-info="mt.photos.v1f1[2]" photo-batch-num="mt.photoBatchNum" ></gsphotosquare>
<gsphotosquare photo-info="mt.photos.v1f1[3]" photo-batch-num="mt.photoBatchNum" ></gsphotosquare>
</div>
Any ideas why clicking on the rendered directive doesn't show the alert?
Try defining your controller a little differently:
myDirectives.controller('Ctrller', Ctrller);
then in your directive:
controller: 'Ctrller as ctrl',