Cannot use ng-repeat inside a directive's template - angularjs

I cannot make my ng-repeat to work inside the directive template. It shows as "ngRepeat: day in days track by $index".
angular
.module('Auto')
.directive('calendar', CalendarDirective);
CalendarDirective.$inject = ['$interpolate'];
CalendarDirectiveController.$inject = ['$scope', '$rootScope', '$attrs', '$location'];
function CalendarDirective($interpolate) {
return {
restrict: 'E',
scope: true,
template: $('#template-calendar').html(),
replace: true,
link: function(scope, element) {
var startSym = $interpolate.startSymbol();
var endSym = $interpolate.endSymbol();
var rawExp = element.html();
var transformedExp = rawExp.replace(/<#/g, startSym).replace(/#>/g, endSym);
var parsedExp = $interpolate(transformedExp);
scope.$watch(parsedExp, function(newValue) {
element.html(newValue);
});
},
controller: CalendarDirectiveController,
};
}
function CalendarDirectiveController($scope, $rootScope, $attrs, $location) {
$scope.monthText = moment().format('MM YYYY');
$scope.days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
$scope.title = function () {
return 'Directive';
}
}
<script type="text/html" id="template-calendar">
<div>
<table>
<thead>
<tr ng-repeat="day in days track by $index">
<th><#day#></th>
</tr>
</thead>
<tbody>
<tr>
</tr>
</tbody>
</table>
</div>
I'm currently out of ideas
Here is the plunker

$interpolateProvider already does the job, a.e.
angular.module('plunker', [], function($interpolateProvider) {
$interpolateProvider.startSymbol('<#');
$interpolateProvider.endSymbol('#>');
})
So you don't need a $watch inside a link.
function CalendarDirective($interpolate) {
return {
restrict: 'E',
scope: true,
template: $('#template-calendar').html(),
replace: true,
link: function(scope, element) {
// removed
},
controller: CalendarDirectiveController,
};
}
Plunker Demo

Related

How to change object name in ng-reapet dynamically in angularjs 1.5

Below is the dynamic directive (it is called in blade.php file)
<div class="col l3 learners-selection-block" id="learners-selection-block">
<dropdown-list
data-name="Learner Type"
data-label="Select Leraner Type"
data-provider='{"adult_learner":"adult Learner","young_learner":"Younge Learner","parent_learner":"Parent Learners"}'
data-provider-search=""
data-provider-type="dataset"
data-class="learner-type-list"
data-id="learner-type-list"
data-search="yes"
data-multiple="yes"
data-keyword="learner-type"
data-model-name="learnertype"
data-obj-name="learnertype"
></dropdown-list>
</div>
<div class="col l3 learners-selection-block" id="learners-selection-block">
<dropdown-list
data-name="Learner"
data-label="Select Learner"
data-provider="getCompanyLearnersList"
data-provider-search="searchNewCompanyLearnersList"
data-provider-type="function"
data-class="select-learner-list"
data-id="select-learner-list"
data-search="yes"
data-multiple="yes"
data-keyword="learner"
data-model-name="learner"
data-obj-name="learner"
></dropdown-list>
</div>
<-- below is the html code that will place on the above directive -->
<div>
<li ng-repeat="record in list_all_records" class="selection-list perf-elem" data-item-id="<%record.id%>" ng-if="search_key == undefined || search_key == '' || record.search_word.toLowerCase().indexOf(search_key) != -1">
</div>
<-- below is the js code to create multiple directive -->
app.directive('dropdownList', ['$filter', function($filter){
return
{
restrict: 'E',
templateUrl: 'js/controllers/get-drop-down-list.html',
transclude: true,
scope: {
main_label: '#',
},
link: function(scope, element, attrs, controller) {
scope.main_label = attrs.name;
scope.select_label = attrs.label;
scope.css_class = attrs.class;
scope.css_id = attrs.id;
scope.label_keyword = attrs.keyword;
scope.list_all_records = attrs.arrayName;
},
controller: function($rootScope, $scope, $http, $attrs, $parse) {
}
}
});
Above code will create two drop down list.
But "list_all_records" in ng-reapet remain same for all drop down
I want to change it "learnertype" on the place of "list_all_records" in first drop down
And I want to change it "learner" on the place of " "list_all_records" in second drop down" in second drop down
So that I can print different-different data in learnertype and in learner
How do I achieve this modification.
I tried and found a code. that is below and it is working for me well.
<dropdown-list
data-name="User Name"
data-label="Please Select"
required ng-model="user_id"
items="user_list"
main-list="main_user_list"
></dropdown-list>
<dropdown-list
data-name="Product Name"
data-label="Please Select"
required ng-model="product_id"
items="product_list"
main-list="main_product_list"
></dropdown-list>
Below Is the Html Code
<div>
<li ng-repeat="record in items" class="selection-list perf-elem" data-item-id="<%record.id%>" ng-if="search_key == undefined || search_key == '' || record.title.toLowerCase().indexOf(title) != -1">
Below is the directive code
app.directive('dropdownList', ['$filter', function($filter){
return{
restrict: 'E',
templateUrl: '/drop-own.html',
transclude: true,
scope: {
items: '=',
ngModel: '=',
mainList: '=',
},
link: function(scope, element, attrs) {
scope.main_label = attrs.name;
},
controller: function($rootScope, $scope, $http, $attrs, $parse) {
}
}
});
Finally declare your all dynamic directive variable in controller as you have added in directive
app.controller('userController', function ($rootScope, $scope, $http, $window, $mdDialog)
{
$scope.user_list = [];
$scope.main_user_list = [];
$scope.user_id = [];
$scope.product_list = [];
$scope.main_product_list = [];
$scope.product_id = [];
});

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>

Child directive can't access parent controller

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.

Angular directive with ng-template

Today I'm trying to develop a popover directive. I don't know why the ng-repeat inside the styles-select directive wich is insered in the popover after click doesn't work(<- Edited it works now)... And I want to get the value of "selectedStyles" in my controller "MyController" without passing it through the directive.
var app = angular.module('MyApp', []);
app.controller('MyController', ['$scope', function($scope) {
$scope.selectedStyles = [];
$scope.$watch('selectedStyles', function(newValue, oldValue) {
console.log(newValue);
});
}]);
app.directive('popover', ['$compile', '$templateCache', function($compile, $templateCache) {
return {
restrict: 'A',
scope: {
header: '#header',
template: '=template'
},
link: function(scope, element) {
element[0].onclick = function (event) {
var popover = document.createElement('div'),
header = document.createElement('h4'),
content = document.createElement('p');
header.textContent = scope.header;
content.innerHTML = $templateCache.get(scope.template);
popover.appendChild(header);
popover.appendChild(content);
document.body.appendChild($compile(popover)(scope)[0]);
scope.$apply();
}
}
};
}]);
app.directive('stylesSelect', ['$compile', '$filter', function($compile, $filter) {
return {
restrict: 'E',
scope: {
selectedStyles: '=selectedStyles'
},
template: '<div ng-repeat="s in styles"><label><input type="checkbox" ng-model="s.selected" ng-change="selectStyle()" /> {{s.label}}</label></div>',
link: function(scope, element) {
scope.styles = [
{label: 'Hipster', selected: false},
{label: 'Hip-Hop', selected: false},
{label: 'Punk', selected: false}
];
scope.selectStyle = function() {
scope.selectedStyles = $filter('filter')(scope.styles, {selected: true});
};
}
}
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="MyController">
{{test}}
<button popover template="'popoverContent.html'" header="Select your styles" type="button">Show Popover</button>
<script type="text/ng-template" id="popoverContent.html">
<styles-select selected-styles="selectedStyles"></styles-select>
</script>
</div>
</div>
It gonna make me crazy... Please Help lol
Thank you
Instead of changing values in different scopes, try to use a service with a promise. This way the popover service is more reusable in your application.
var app = angular.module('MyApp', []);
app.controller('MyController', ['$scope', 'popover',
function($scope, popover) {
$scope.selectedStyles = [];
$scope.showStylesSelect = function() {
popover.show({
templateUrl: 'popoverContent.html',
scope: {
header: 'Select your style',
styles: [{
label: 'Hipster',
selected: false
}, {
label: 'Hip-Hop',
selected: false
}, {
label: 'Punk',
selected: false
}]
}
}).then(function(result) {
$scope.selectedStyles = result.selectedStyles;
});
};
$scope.$watch('selectedStyles', function(newValue, oldValue) {
console.log(newValue);
});
}
]);
app.factory('popover', ['$rootScope', '$q', '$compile', '$templateCache',
function($rootScope, $q, $compile, $templateCache) {
function showPopover(options) {
var defer = $q.defer(),
scope = $rootScope.$new(),
popover = document.createElement('div'),
header = document.createElement('h4'),
content = document.createElement('p');
angular.extend(scope, options.scope || {});
scope.close = function() {
popover.parentNode.removeChild(popover);
defer.resolve(scope);
};
header.textContent = options.header || '';
content.innerHTML = $templateCache.get(options.templateUrl);
popover.appendChild(header);
popover.appendChild(content);
document.body.appendChild($compile(popover)(scope)[0]);
return defer.promise;
}
return {
show: showPopover
}
}
]);
app.directive('stylesSelect', ['$filter',
function($filter) {
return {
restrict: 'E',
scope: false,
template: '<div ng-repeat="s in styles"><label><input type="checkbox" ng-model="s.selected" ng-change="selectStyle()" /> {{s.label}}</label></div><button ng-click="close()">close</button>',
link: function(scope) {
scope.selectStyle = function() {
scope.selectedStyles = $filter('filter')(scope.styles, {
selected: true
});
};
}
}
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
<div ng-app="MyApp" class="ng-scope">
<script type="text/ng-template" id="popoverContent.html">
<styles-select selected-styles="selectedStyles"></styles-select>
</script>
<div ng-controller="MyController" class="ng-scope ng-binding">
<button ng-click="showStylesSelect()">Show Popover</button>
</div>
</div>

How to dynamically include controller from string for directives?

I need to dynamically choose the controller at ngRepeat based on component name as a string. Plunker: http://plnkr.co/edit/osLLf0c4eNCpNokyyXCI?p=preview
As you can see I found a solution, but I don't think this is an Angular way, because I've created a stack of controllers and assign them to "controllers" variable outside the angular scope.
Here is HTML:
<table border="1" class="item" data-ng-repeat="component in components">
<tr>
<td>
<div component="component"></div>
</td>
</tr>
</table>
Javascript
var app = angular.module("sortableApp", []);
app.controller("appCtrl", ["$scope", function($scope) {
$scope.components = [{
name: 'simpleText',
tpl: 'simple-text.html',
content: 'Simple text'
}, {
name: 'heading',
tpl: 'heading.html',
content: 'Heading'
}];
}]);
app.directive("component", ["$templateCache", "$compile", function($templateCache, $compile) {
return {
restrict: "A",
scope: {
component: "="
},
controller: function($scope) {
if(controllers[$scope.component.name]) {
return controllers[$scope.component.name]($scope);
}
},
link: function(scope, element, attrs) {},
template: '<div ng-include="component.tpl"></div>'
}
}]);
var controllers = {
simpleText: function($scope) {
$scope.add = function() {
alert('add text');
}
},
heading: function($scope) {
$scope.add = function() {
alert('add heading');
}
}
}
How to achieve my goal in angular way? thanks for your answer!

Resources