Child directive can't access parent controller - angularjs

i have 2 directives that create a grid table.
For each row we may have some actions and my question is can i access in some way those actions without calling $parent.$parent.action in my template?
.directive("grid", function ($compile, $parse) {
return {
restrict: 'AE',
scope: {
templateUrl : '#?',
columns : '=',
filterBy : '=?',
excludeSortColumns : '=?'
},
controller: GridController,
controllerAs: 'grid',
bindToController: true,
replace: true,
transclude: true,
templateUrl: function($element, $attrs) {
if(!angular.isDefined($attrs.filterBy)) {
return '/assets/js/src/templates/partials/grid/grid-sortable.tpl.html';
}
return $attrs.templateUrl || '/assets/js/src/templates/partials/grid/grid.tpl.html';
},
link: function($scope, $element, $attrs, ctrl, transclude) {
//filter collection when user has typed some letter
$element.on('keyup', function() {
$scope.$apply(function() {
$scope.$parent.filterValue = $('.filterBy').val();
});
});
//var e = $compile(transclude())($scope.$parent);
//angular.element('.table').append(e);
$element.append(transclude($scope.$parent));
}
};
function GridController($scope, $element, $attrs) {
var vm = this;
vm.columns = $parse($attrs.columns)($scope.$parent);
vm.excludeSortColumns = angular.isDefined($attrs.excludeSortColumns) ? $parse($attrs.excludeSortColumns)($scope.$parent) : false;
vm.filterCol = angular.isDefined($attrs.filterBy) ? $parse($attrs.filterBy)($scope.$parent) : false;
vm.sortBy = function(column) {
if(!vm.isExludedSortBy(column)) {
vm.predicate = column;
vm.reverse = (vm.predicate === column) ? !vm.reverse : false;
var sortType = (vm.reverse) ? '-' : '+';
$scope.$parent.orderByColumn = sortType + column;
}
};
vm.isExludedSortBy = function(column) {
if(!vm.excludeSortColumns) {
return true;
}
if(vm.excludeSortColumns.indexOf(column) >= 0) {
return true;
}
return false;
};
};
})
.directive("gridBody", function ($parse) {
return {
restrict: 'AE',
scope: {
templateUrl : '#?',
collection : '='
},
require: '^grid',
controller: function($scope, $element, $attrs) {
var vm = this;
vm.collection = $parse($attrs.collection)($scope.$parent);
},
controllerAs: 'body',
bindToController: true,
replace: true,
transclude: false,
templateUrl: function($element, $attrs) {
return $attrs.templateUrl || '/assets/js/src/templates/partials/grid/grid-body.tpl.html';
}
};
})
This is a template:
<tbody>
<tr ng-repeat="item in body.collection | gridFilterBy:$parent.filterBy:$parent.filterValue | orderBy:$parent.orderByColumn track by $index">
<td class="col-sm-1">{{item.id}}</td>
<td class="col-sm-2">{{item.name}}</td>
<td class="col-sm-1">{{item.latest_result.count}}</td>
<td class="col-sm-2">{{item.created_at}}</td>
<td class="col-sm-2">{{item.updated_at}}</td>
<td class="col-sm-4">
<button class="btn btn-primary" ng-disabled="item.has_cluster" role="button" ng-click="$parent.$parent.addCluster(item.id)">
Crea cluster
</button>
<button class="btn btn-primary" role="button" ng-click="$parent.getExports(item.id, item.name)">
Export
</button>
<button class="btn btn-primary" role="button" ng-click="$parent.$parent.loadQuery(item.id)">
Visualizza
</button>
<button class="btn btn-primary" role="button" ng-click="$parent.$parent.removeQuery(item.id)">
Rimuovi
</button>
</td>
</tr>
</tbody>
<grid columns="columns" filter-by="filterBy" exclude-sort-columns="excludeColumns">
<grid-body collection="result.items" template-url="/assets/js/src/templates/partials/grid/grid-body.tpl.html"></grid-body>
</grid>
i can't get grid controller
Thanks for all replies.

Typically this is done through the directive controllers. The following is an example of setting up parent and child directive controllers to be aware of each other:
app.directive('parentDir', function() {
return {
restrict: 'A',
controller: function($scope, $element, $attrs) {
this.parentInit = function(ctrl) {
console.log(ctrl);
};
this.parentClick = function(param) {
};
},
link: function(scope, element, attrs) {
}
}
});
app.directive('childDir', function() {
return {
restrict: 'A',
require: ['childDir', '^parentDir'],
controller: function($scope, $element, $attrs) {
var parentCtrl;
this.childInit = function(parentCtrl) {
console.log(parentCtrl);
parentCtrl = ctrl;
};
$scope.childClick = function(param) {
parentCtrl.parentClick(param);
};
},
link: function(scope, element, attrs, ctrls) {
ctrls[0].childInit(ctrls[1]);
ctrls[1].parentInit(ctrls[0]);
}
}
});
Here is a plunk with a demo.

Related

Update child directive on click

is there a way to update a child directive on click? In my plnkr, column 1 contains a list of names. If you click on the name it will populate the info into the contact directive in column 2. If I make a change in the textbox in column 2, the data in the info directive in column 3 will also change as well. Here is my plnkr:
http://plnkr.co/edit/gcZbd9letYhA4ViBQJ0Q?p=preview
Here is my JS:
var app = angular.module('myApp', []);
app.controller('contactController', function() {
this.contacts = [
{
id: 1,
name: 'Bob'
},
{
id: 2,
name: 'Sally'
},
{
id: 3,
name: 'Joe'
}
]
this.selectedContact;
this.PublishData = function(data) {
this.selectedContact = data;
}
this.UpdateData = function(data) {
for (var i = 0; i < this.contacts.length; i++) {
if (this.contacts[i].id === data.id) {
this.contacts[i] = angular.copy(data);
}
}
}
});
app.directive('contactDirective', function () {
return {
restrict: 'E',
templateUrl: 'contact.html',
scope: {
myModel: '=',
updateData: '&'
},
link: function (scope, element, attrs) {
scope.$watch('myModel', function (newValue, oldValue) {
scope.contact = angular.copy(newValue);
});
}
}
});
app.directive('infoDirective', function () {
return {
restrict: 'E',
templateUrl: 'info.html',
scope: {
contactObject: '='
},
link: function (scope, element, attrs) {
}
}
});
you can simply use $broadcast and $on services
with $broadcat you create an event and you pass a parameter
with $on you listen that event and take that value
I edited your code in this way:
<!--Contact template-->
<div class="col-sm-6">
<b>Column 2</b>
<input type="text" ng-model="newName" />
<button ng-click="updateData({data:contact,newName:newName})">Update</button>
</div>
<div class="col-sm-6">
<b>Column 3</b>
<info-directive contact-object="contact"></info-directive>
</div>
<!-- Your Index file -->
<body ng-app="myApp">
<div ng-controller="contactController as $ctrl">
<div class="row col-md-12">
<div class="col-sm-2">
<b>Column 1</b>
<div ng-repeat="contact in $ctrl.contacts">
</div>
</div>
<div class="col-sm-6">
<contact-directive my-model="$ctrl.selectedContact" update-data="$ctrl.UpdateData(data,newName)"></contact-directive>
</div>
</div>
</div>
</body>
//and your controller
var app = angular.module('myApp', []);
app.controller('contactController', function() {
this.contacts = [
{
id: 1,
name: 'Bob'
},
{
id: 2,
name: 'Sally'
},
{
id: 3,
name: 'Joe'
}
]
this.selectedContact;
this.PublishData = function(data) {
this.selectedContact = data;
}
this.UpdateData = function(data,newName) {
for (var i = 0; i < this.contacts.length; i++) {
if (this.contacts[i].id === data.id) {
this.contacts[i].name = newName;
}
}
}
});
app.directive('contactDirective', function () {
return {
restrict: 'E',
templateUrl: 'contact.html',
scope: {
myModel: '=',
updateData: '&'
},
link: function (scope, element, attrs) {
scope.$watch('myModel', function (newValue, oldValue) {
scope.newName = newValue.name;
scope.contact = angular.copy(newValue);
});
}
}
});
app.directive('infoDirective', function () {
return {
restrict: 'E',
templateUrl: 'info.html',
scope: {
contactObject: '='
},
link: function (scope, element, attrs) {
}
}
});

Linking function from controller scope in directive scope

I've wrote a directive that should emulate the AngularJS-DataTable.
In this case I need to execute some function on the last <td> since they're buttons. I don't want to pass the functions to the directive to keep the directive as independet as possible.
So in this case, when I specify "renderable" on a data, and a "render" function, if it got a ng-click I need that function, defined in the controller, to be executed, but when i Click on the buttons, nothing happens.
This is the data I've in my Controller, with the function "print()" that I need to call from the directive
$scope.print = function(){
console.log("It worked!");
};
$scope.tableData = {
data: data.response,
columns: [{
title:"",
data: "priority",
renderable: true,
render: function(data){
return "<span class='btn btn-xs fa fa-fw fa-angle-down' ng-click='lowerPriority()'></span>";
}
},
{
title: "Nome Servizio",
data: "title"
},
{
title: "Descrizione",
data: "description",
renderable: true,
render: function(data, row){
var html = "<div ng-click='print()'>"+row.sum+"</div>";
return html;
}
},
],
}
In my page I'm calling
<smart-table data="tableData" ></smart-table>
And then in my directive template
<tr ng-repeat="row in data.data | filter: search.value" repeat-done>
<td ng-repeat="cell in data.columns">
<span ng-if="cell.renderable" ng-bind-html="trustHtml(cell.render(row[cell.data], row))"></span>
<span ng-if="!cell.renderable">{{row[cell.data]}}</span>
</td>
</tr>
Lastly, this is my directive
var smartTable = angular.module('smartTable', ['ngSanitize']);
smartTable.directive('smartTable',['$compile', '$sce', '$templateRequest', function($compile, $sce, $templateRequest) {
return {
restrict: 'AE',
replace: true,
templateUrl: '/public/components/directives/smartTable.tpl.html',
link: function(scope, elem, attrs, parentScope) {
scope.trustHtml = function(data){
var template = angular.element(data);
elem.append(template);
// $compile(angular.element(data))(scope);
return $sce.trustAsHtml(data);
};
$templateRequest('/public/components/directives/smartTable.tpl.html').then(function(html){
console.log(scope);
scope.$watch(attrs.data, function(elemasd) {
var template = angular.element(html);
elem.append(template);
elem.html(html);
scope.data = scope[attrs.data];
$compile(elem)(scope);
});
});
}
};
}]);
Ater your template slightly to use $last, if you're looking for the last td:
<td ng-if="$last" ng-click="vm.print()"></td>
Do something like this bind your function
smartTable.directive('smartTable',['$compile', '$sce', '$templateRequest', function($compile, $sce, $templateRequest) {
return {
restrict: 'AE',
replace: true,
scope: {data: '=',
print: '&'},
templateUrl: '/public/components/directives/smartTable.tpl.html',
link: function(scope, elem, attrs, parentScope) {
scope.trustHtml = function(data){
var template = angular.element(data);
elem.append(template);
// $compile(angular.element(data))(scope);
return $sce.trustAsHtml(data);
};
$templateRequest('/public/components/directives/smartTable.tpl.html').then(function(html){
console.log(scope);
scope.$watch(attrs.data, function(elemasd) {
var template = angular.element(html);
elem.append(template);
elem.html(html);
scope.data = scope[attrs.data];
$compile(elem)(scope);
});
});
}
};
}]);
HTML
<smart-table data="tableData" print="print"></smart-table>

AngularJS directive is adding an extra whitespace character, how can I remove it?

You can see the problem at this page I am developing. It occurs after the characters 勝 and 生. I don't understand where this extra space character is coming from or how to eliminate it. Any help is appreciated.
'use strict';
var app = angular.module("readerApp", ['ui.bootstrap', 'ngAnimate']);
app.controller('ReaderCtrl', function ($scope) {
$scope.knownKanji = 0;
$scope.definition = "";
$scope.dictionary = {
'生きる': {
meaning: 'To live',
sample: '生きる意味を教えてくれ。',
kanji: {
'生' : 'live'
}
},
'勝つ': {
meaning: 'To win',
sample: '明日は勝てないかもしれない。',
kanji: {
'勝': 'win'
}
}
};
$scope.$on('kanjiUp', function(event, data) {
$scope.knownKanji += 1;
});
$scope.$on('kanjiDown', function(event, data) {
$scope.knownKanji -= 1;
});
$scope.$on('setDefinition', function(event, data) {
$scope.definition = $scope.dictionary[data];
console.log($scope.definition);
});
}).directive('kanji', function() {
return {
transclude: true,
restrict: 'E',
templateUrl: 'static/js/kanji.html',
scope: true,
controller: function($scope, $transclude, $element) {
},
link: function(scope, element, attrs, ctrl, transclude) {
scope.reading = attrs['reading'];
var clickingCallback = function() {
if (scope.reading=='') {
scope.reading = attrs['reading'];
scope.$emit('kanjiDown');
}
else {
scope.reading='';
scope.$emit('kanjiUp');
}
scope.$apply();
};
element.bind('click', clickingCallback);
}
};
}).directive('line', function() {
return {
transclude: true,
restrict: 'E',
templateUrl: 'static/js/line.html',
controller: function($scope, $transclude, $element) {
$scope.isCollapsed = true;
},
link: function(scope, element, attrs, ctrl, transclude) {
scope.translation = attrs['translation'];
}
};
}).directive('word', function() {
return {
transclude: true,
restrict: 'E',
templateUrl: 'static/js/word.html',
controller: function($scope, $transclude, $element) {
},
link: function(scope, element, attrs, ctrl, transclude) {
element.bind('click', function(e) {
var el = element.clone();
var elementClone = el[0];
el.find('rt').remove();
var word = el.text().replace(/(\r\n|\n|\r)/gm,"");
scope.$emit('setDefinition', word);
});
}
};
});
line.html
<div class="line">
<p>
<span ng-transclude></span>
</p>
<i class="glyphicon"
ng-click="isCollapsed = !isCollapsed"
ng-class="{'glyphicon-chevron-up': !isCollapsed, 'glyphicon-chevron-down': isCollapsed}">
翻訳
</i>
<div uib-collapse="isCollapsed">
<div class="well well-sm">{{translation}}</div>
</div>
word.html
<span ng-transclude></span>
kanji.html
<ruby><span ng-transclude></span><rt>{{reading}}</rt></ruby>
On the linked page, there is no extra space character. The visible gap is created by the used font. You might try a CSS rule to get rid of the extra space,
kanji {margin-right: -12px;} for example..
As #Claies pointed out in the comments, the extra space is produced by the span element of the directive. The directive needs to be cofigured with replace: true to remove the directive element.
See the AngularJS documentation for $compile for details.

AngularJs ng-transclude orphan issue

Ive gotten a problem with ng-transcule orphan issue, wich directed me to this link:
https://docs.angularjs.org/error/ngTransclude/orphan?p0=%3Cng-transclude%3E
This happened when trying to implement a tab directive I saw on thinkster.io, into the code of the shaping up with Angularjs course of codeschool.
I've must have donse something wrong but can't figure out what exactly.
I implemented it creating a new module called tab and making it a dependency on the product-store module, this is part of the code:
index.html:
<body ng-controller="StoreController as store">
<h1 ng-bind="'Welcome' | capitalize"></h1>
<ul class="list-group">
<li class="list-group-item" ng-repeat="product in store.products | filter: store.search | orderBy: '-price'" ng-hide="product.soldOut">
<h3>
<product-title product="product"></product-title>
</h3>
<product-panels product="product"></product-panels>
</li>
</ul>
</body>
</html>
codeschoolapp.js:
(function(){
angular.element(document).ready(function() {
angular.bootstrap(angular.element("body")[0], ['store'], {
strictDi: true
});
});
var store = angular.module('store', ['store-products']);
store.controller('StoreController', ['$http', function($http){
var vm = this;
vm.products = [];
$http.get('json/products.json').success(function(data){
vm.products = data;
});
}]);
store.filter('capitalize', function(){
return function (text) {
return text.toUpperCase();
};
});
})();
product.js:
(function(){
var store = angular.module('store-products', ['tab']);
store.directive("productPanels", function(){
return {
restrict: 'E',
templateUrl: "product-panels.html",
scope: { "product" : "=" }
};
});
store.directive("productDescription", function(){
return {
restrict: 'E',
scope: { "product" : "=" },
templateUrl: "product-description.html"
};
});
store.directive("productSpecs", function(){
return {
restrict: 'E',
scope: { "product" : "=" },
templateUrl: "product-specs.html"
};
});
store.directive("productReviews", function(){
return {
restrict: 'E',
scope: { "product" : "=" },
templateUrl: "product-reviews.html"
};
});
store.directive("productTitle", function(){
return {
restrict: 'E',
scope: { "product" : "=" },
templateUrl: "product-title.html"
};
});
})();
tab.js:
(function(){
var tab = angular.module('tab', []);
tab.directive('tab', function(){
return {
restrict: 'E',
transclude: true,
templateUrl: 'tab.html',
require: '^tabset',
scope: {
heading: '#'
},
link: function(scope, elem, attr, tabsetCtrl) {
scope.active = false;
tabsetCtrl.addTab(scope);
}
};
});
tab.directive('tabset', function() {
return {
restrict : 'E',
tranclude : true,
scope: {
type: '#'
},
templateUrl: 'tabset.html',
bindToController: true,
controllerAs: 'tabset',
controller: function() {
var self = this;
self.tabs = [];
self.classes = {};
if(self.type === 'pills') {
self.classes['nav-pills'] = true;
}
else {
self.classes['nav-tabs'] = true;
}
self.addTab = function (tab){
self.tabs.push(tab);
if(self.tabs.length === 1) {
tab.active = true;
}
};
self.select = function(selectedTab) {
angular.forEach(self.tabs, function(tab) {
if(tab.active && tab !== selectedTab) {
tab.active = false;
}
});
selectedTab.active = true;
};
}
};
});
})();
relevant templates:
tabset.html:
<div role="tabpanel">
<ul class="nav" ng-class="tabset.classes" role="tablist">
<li role="presentation"
ng-repeat="tab in tabsett.tabs"
ng-class="{'active': tab.active}">
<a href=""
role="tab"
ng-click="tabsett.select(tab)">{{tab.heading}}</a>
</li>
</ul>
<ng-transclude>
</ng-transclude>
</div>
tab.html:
<div role="tabpanel" ng-show="active" ng-transclude></div>
product-panels.html:
<tabset type="pills">
<tab heading="Description">
<product-description product="product"></product-description>
</tab>
<tab heading="Specifications">
<product-specs product="product"></product-specs>
</tab>
<tab heading="Reviews">
<product-reviews product="product"></product-reviews>
</tab>
</tabset>
Also here is a plunker with everything
The error's link says that I must be missing a transclude : true, but the DDOs do have that, and haven't been able to find much on the issue.
Thanks for the help
There is just a minor spelling error in tab.js : tranclude : true, should be transclude : true,

How can I access to ngModel of an input from a parent Directive / angularjs

I have this view
<div my-directive="somevalue">
<input name="myField" ng-model="dataForMyField">
</div>
And this is my directive
app.directive('myDirective', function ($compile) {
return {
restrict: 'A',
template: `<div ng-transclude=""></div>
<div>SomeValue from directive <strong>{{ someReturnedValue }}</strong></div>`,
transclude: true,
controller: function($scope, $element, $attrs, $transclude) {
$scope.someReturnedValue = 'ValueFromDirective';
console.log('Name of input'); // myField
$scope.$watch('vm.ngModel', function(newValue, oldValue, scope) {
console.log('WOW! Input.ngModel changed', newValue); // world in the init
});
}
}
})
How can I access to ngModel of input.
---------> here is a plkr: http://plnkr.co/edit/nWNAuf9jbv0sgY2VYRtZ?p=preview
Try this
<div my-directive="somevalue">
<input name="myField" ng-model="dataForMyField">
</div>
app.directive('myDirective', function () {
return {
scope: {
dataForMyField: '#dataAttr'
},
restrict: 'A',
template: `<div ng-transclude=""></div>
<div>SomeValue from directive <strong>{{ someReturnedValue }}</strong></div>`,
transclude: true,
controller: function($scope, $element, $attrs, $transclude) {
$scope.someReturnedValue = 'ValueFromDirective';
console.log('Name of input'); // myField
}
}
})

Resources