I'm new to angular, and I'm facing some problem while making the directives to communicate, getting Cannot bind to controller without directive 'tabset's controller
Here is my Code
HTML code
<tabset>
<tab heading="Tab 1">
Hi
</tab>
<tab heading="Tab 2">
Second tab
</tab>
</tabset>
Directive for the above code
angular.module('testApp')
.directive('tab', function(){
return{
restrict: 'E',
transclude: true,
template: '<h1>Hello Developer!!</h1><div role="tabpanel"><ng-transclude></ng-transclude></div>',
require: '^tabset',
scope: {
heading: '#'
},
link: function(scope, elem, attrs, tabsetCtrl){
tabsetCtrl.addTab(scope);
}
}
}).directive('tabset', function(){
return{
restrict: 'E',
transclude: true,
scope: { },
templateUrl: 'views/tabset.html',
bindToController: true,
controllerAs: 'tabset',
contrller: function(){
var self = this;
this.tabs = [];
self.addTab = function addTab(tab)
{
self.tabs.push(tab);
}
}
}
});
Thanks in advance.
I believe this is happening because you spelled "controller" wrong. Under controllerAs, you have "contrller"
Related
I coded two nested directives, the outer one is wrapping all inner ones to build a nav. But seems the ng-transclude all inner directives. I have debugged a while, but can't figure it out. Below are the directives and the html snippet;
angular.module('custom.directives', [])
.directive('navTabs', ['$log', function($log){
$log.info('instantaiting directive navTabs...');
var _directive = {
scope: true,
restrict: 'EA',
replace: true,
transclude: true,
templateUrl: '/jsex/ab/templates/tabs.html',
controller: ['$scope', function($scope) {
$scope.currentTab = 0;
$scope.tabs = [];
$scope.selectTab = function(tab) {
$scope.currentTab = tab;
}
return $scope;
}]
};
$log.info('finish instantaiting directive navTabs');
return _directive;
}])
.directive('navTab', ['$log', '$filter', function($log, $filter){
$log.info('instantaiting directive navTab...');
var _directive = {
scope: true,
restrict: 'EA',
replace: true,
transclude: true,
require: '^navTabs',
template: '<div ng-show="showTab()" ng-transclude></div>',
link: function(scope, element, attrs, navTabs) {
var tabId = navTabs.tabs.length;
$log.info("tabId = " + tabId + " - " + attrs.tabHeading);
navTabs.tabs.push(attrs.tabHeading);
$log.info($filter('json')(navTabs.tabs));
scope.showTab = function() {
return tabId == scope.currentTab;
};
}
};
$log.info('finish instantaiting directive navTab');
return _directive;
}]);
<nav-tabs>
<nav-tab class="tab-content" tab-heading="First Tab">
<h1> First Tab</h1>
<p>Simple tab 1
</nav-tab>
<nav-tab class="tab-content" tab-heading="Second Tab">
<h1> Second Tab</h1>
<p>Simple tab 2
</nav-tab>
<nav-tab class="tab-content" tab-heading="Third Tab">
<h1> Third Tab</h1>
<p>Simple tab 3
</nav-tab>
</nav-tabs>
Thanks.
I'm relatively new to AngularJS and working on creating tabs in a page. Till now I have resolved my problems with angularjs by searching a lot on internet but I can't resolve this. Hope anyone can help me with ideas and better knowledge of angularjs.
I have two custom directives tabset and tab. 'Tabset' is the directive to maintain the tabs and 'tab' is for a single tab.
app.directive('tabset', function() {
return {
restrict: 'E',
transclude: true,
templateUrl: 'tabset.html',
bindToController: true,
scope: {},
controller: function($scope){
$scope.tabs = [];
this.addTab = function(tab) {
$scope.tabs.push(tab);
}
console.log("In tabset controller");
},
link : function(scope){
console.log("In the tabset link");
}
}
});
//Custom Directive for the tab controls
app.directive('tab', function() {
return {
restrict: 'E',
transclude: true,
template: '<h2>Welcome to Stackoverflow</h2> <div role="tabpanel" ng-transclude></div>',
require : '^tabset',
scope: {},
link : function(scope, elem, attr, tabsetCntrl) {
tabsetCntrl.addTab(scope);
console.log("In the tab link");
}
}
});
I call these directives in my HTML page as shown below:
<tabset>
<tab>
This is one tab
</tab>
<tab>
This is another tab
</tab>
</tabset>
But, when I run the code, the link function of the tab directive is not running. The 'require : ^tabset' option gets the controller from the tabset, but the link function of the tab directive is not working.
Try adding controllerAs: '$ctrl' to your tabset directive.
Like:
angular.module('app').directive('tabset', function() {
return {
restrict: 'E',
transclude: true,
templateUrl: 'tabset.html',
bindToController: true,
controllerAs: '$ctrl', // <---- HERE
scope: {},
controller: function($scope){
$scope.tabs = [];
this.addTab = function(tab) {
$scope.tabs.push(tab);
}
console.log("In tabset controller");
},
link : function(scope){
console.log("In the tabset link");
}
}
});
Tested
Further info found by checking the error seen in console here
Prudhvee, take a look at this demo i did to understand the making of angular tabs using nested directives.
app.directive('tabset', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: [ "$scope", function($scope) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
}
this.addPane = function(pane) {
if (panes.length == 0) $scope.select(pane);
panes.push(pane);
}
}],
template:
'<div class="tabbable">' +
'<ul class="nav nav-tabs">' +
'<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">'+
'{{pane.title}}' +
'</li>' +
'</ul>' +
'<div class="tab-content" ng-transclude></div>' +
'</div>',
replace: true
};
});
app.directive('tab', function() {
return {
require: '^tabset',
restrict: 'E',
transclude: true,
scope: { title: '#' },
link: function(scope, element, attrs, tabsCtrl) {
tabsCtrl.addPane(scope);
},
template:
'<div class="tab-pane" ng-class="{active: selected}" ng-transclude>' +
'</div>',
replace: true
};
})
http://plnkr.co/edit/BJWWw2?p=preview
How i can show/hide directive when some elements in parent controller has event?
app.directive('rest', function(){
return {
restrict: 'E',
scope: false,
replace: true,
link: function(scope, elem, attr) {
},
templateUrl: '<div ng-show="showDirective"></div>',
}
});
<div ng-controller="AppCtrl">
<rest></rest>
<div ng-click="showDirective = false"><div> <!-- hide directive -->
<div ng-click="showDirective = true"><div> <!-- show directive -->
</div>
Here, your mistake was "templateUrl" instead of "template". When you provide html inline, user "template", when you provide html in a separate html file, use "templateUrl" and provide a url to the template html:
http://plnkr.co/edit/xzqOvcjKYfWxazWfHWyS?p=preview
.directive('rest', function(){
return {
restrict: 'E',
scope: false,
replace: true,
link: function(scope, elem, attr) {
},
template: '<div ng-if = "showDirective" >Test</div>',
}
I've found plenty examples of calling controller function from directive however couldn't find an example of calling it from directive template.
Let's say I've got this HTML code to open modal directive
<button ng-click='toggleModal()'>Open</button>
<modal-dialog show='modalShown' confirm="confirmCtrl()">
<p>Modal Content Goes here<p>
</modal-dialog>
Here's my controller with a function confirmCtrl() I want to call:
myApp.controller('myController', function($scope){
$scope.modalShown = false;
$scope.toggleModal = function() {
$scope.modalShown = !$scope.modalShown;
};
$scope.confirmCtrl = function () {
alert('confirmed');
}
})
And here's my directive. I
.directive('modalDialog', function(){
return {
restrict: 'E',
scope: {
show: '=',
corfirm: '&'
},
replace: true,
transclude: true,
link: function(scope, element, attrs) {
scope.hideModal = function() {
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal()'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div><button ng-click=""> Confirm </button></div></div>"
};
In my template, I've got a button and I want to call confirmCtrl() function on click, however, couldn't grasp how to do it
Here's a working fiddle http://jsfiddle.net/anao4nsw/
You can define your controller in your directive like so, and add an ng-click directive to the button element 'Confirm' in your template.
.directive('modalDialog', function(){
return {
controller: 'myController' //This line.
restrict: 'E',
scope: {
show: '=',
corfirm: '&'
},
replace: true,
transclude: true,
link: function(scope, element, attrs) {
scope.hideModal = function() {
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal()'></div><div class='ng-modal-dialog' ng-style='dialogStyle'>
<div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div>
<button ng-click='confirmCtrl()'> Confirm </button></div></div>"
Note the addition of the ng-click='confirmCtrl()' in the last line of your template.
You've almost done what you need. The &-binding does the trick: it assigns a function to the property of the isolate scope and this function when called executes the expression specified in the attribute. So in your template, you just have to call this function of the isolate scope in ng-click: <button ng-click="confirm()"> Confirm </button>. It might not work for you because of a typo: you have coRfirm: '&' instead of coNfirm: '&'.
Your directive it would look so, this work very good. Test it, only copy and replace the current directive
app.directive('modalDialog', function(){
return {
restrict: 'E',
scope: {
show: '=',
confirm: '&'
},
replace: true,
transclude: true,
link: function(scope, element, attrs) {
scope.hideModal = function() {
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'><div class='ng-modal-overlay' ng-click='hideModal()'></div><div class='ng-modal-dialog' ng-style='dialogStyle'><div class='ng-modal-close' ng-click='hideModal()'>X</div><div class='ng-modal-dialog-content' ng-transclude></div><button ng-click='confirm()'> OK </button></div></div>"
};
});
This should not be too hard a thing to do but I cannot figure out how best to do it.
I have a parent directive, like so:
directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
replace: true,
transclude: true,
template: '
<div class="editable-fieldset" ng-click="edit()">
<div ng-transclude></div>
...
</div>',
controller: ['$scope', function ($scope) {
$scope.edit = ->
$scope.editing = true
// ...
]
};
});
And a child directive:
.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
template: function (element, attrs) {
'<div>
<label>' + attrs.label + '</label>
<p>{{ model.' + attrs.field + ' }}</p>
...
</div>'
},
require: '^editableFieldset'
};
});
How can I easily access the model and editing properties of the parent directive from the child directive? In my link function I have access to the parent scope - should I use $watch to watch these properties?
Put together, what I'd like to have is:
<editable-fieldset model="myModel">
<editable-string label="Some Property" field="property"></editable-string>
<editable-string label="Some Property" field="property"></editable-string>
</editable-fieldset>
The idea is to have a set of fields displayed by default. If clicked on, they become inputs and can be edited.
Taking inspiration from this SO post, I've got a working solution here in this plunker.
I had to change quite a bit. I opted to have an isolated scope on the editableString as well because it was easier to bind in the correct values to the template. Otherwise, you are going to have to use compile or another method (like $transclude service).
Here is the result:
JS:
var myApp = angular.module('myApp', []);
myApp.controller('Ctrl', function($scope) {
$scope.myModel = { property1: 'hello1', property2: 'hello2' }
});
myApp.directive('editableFieldset', function () {
return {
restrict: 'E',
scope: {
model: '='
},
transclude: true,
replace: true,
template: '<div class="editable-fieldset" ng-click="edit()"><div ng-transclude></div></div>',
link: function(scope, element) {
scope.edit = function() {
scope.editing = true;
}
},
controller: ['$scope', function($scope) {
this.getModel = function() {
return $scope.model;
}
}]
};
});
myApp.directive('editableString', function () {
return {
restrict: 'E',
replace: true,
scope: {
label: '#',
field: '#'
},
template: '<div><label>{{ label }}</label><p>{{ model[field] }}</p></div>',
require: '^editableFieldset',
link: function(scope, element, attrs, ctrl) {
scope.model = ctrl.getModel();
}
};
});
HTML:
<body ng-controller="Ctrl">
<h1>Hello Plunker!</h1>
<editable-fieldset model="myModel">
<editable-string label="Some Property1:" field="property1"></editable-string>
<editable-string label="Some Property2:" field="property2"></editable-string>
</editable-fieldset>
</body>
You can get access to parent controller by passing attribute in child directive link function
link: function (scope, element, attrs, parentCtrl) {
parentCtrl.$scope.editing = true;
}