ng-repeat do not evaluate from $sce.trustAsHtml inside directive - angularjs

I have a directive that has a piece of custom HTML that I can pass in as a custom option. I would like to use ng-repeat inside the custom HTML, but it is not being displayed. Here is the code.
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.local = {
options: {
columns: [{
name: 'product_id',
label: 'Product ID'
}, {
name: 'product_name',
label: 'Name'
}],
getBody: function( col, i ) {
return col.name == 'product_id' ? col.label : '<div class="dropdown product-status"><button class="btn btn-primary dropdown-toggle" data-toggle="dropdown">Status<span class="caret"></span></button><span class="dropdown-arrow"></span><ul class="dropdown-menu"><li ng-repeat="one_status in dataset.status"><a class="status all" ng-click="dataset.updateStatus(one_status.status_id)" ng-bind="one_status.status_name"></a></li></ul></div>';
}
},
dataset: {
status: [{
status_id: 1,
status_name: 'first draft'
}, {
status_id: 2,
status_name: 'in edit'
}]
}
};
});
app.directive('test', function($sce) {
var linkFunc = function(scope) {
scope.getBody = function( col, index ) {
return $sce.trustAsHtml( scope.options.getBody( col, index ) );
};
};
return {
link: linkFunc,
scope: {
options: '=',
dataset: '='
},
templateUrl: 'test.html'
};
});
http://plnkr.co/edit/mCynQWtQElTGfoLXZHqZ?p=preview

you need to make sure $sce is in a part of your controller
check this out
-- http://jsfiddle.net/3J25M/2/
.controller('ngBindHtmlCtrl', ['$scope','$sce', function ngBindHtmlCtrl($scope, $sce)
...etc
also, check this question out: AngularJS using $sce.trustAsHtml with ng-repeat

Related

Angular recursive directive to represent tree structure needs adding file list

I have been attempting to update this directive to add fileList into the tree structure, but I was not successful. The original works fine (https://embed.plnkr.co/plunk/JgQu3r), but it considers directories only. In my data structure any directory can have a fileList, as well as any number of nested directoryList and so on.
function MainController($scope) {
$scope.menu = [{
name: 'one',
directoryList: [{
name: 'one'
}, {
name: 'two'
}],
fileList: ['one.pdf', 'two.pdf', 'three.pdf']
},
{
name: 'two',
directoryList: [{
name: 'one',
directoryList: [{
name: 'one'
}],
fileList: ['one.pdf', 'two.pdf', 'three.pdf', 'four.pdf']
}]
},
{
name: 'three'
}
];
}
function myMenu() {
return {
scope: {
myMenu: '=myMenu'
},
template: '<li ng-repeat="item in myMenu"><my-menu-item></my-menu-item></li>',
link: function(scope, elem) {}
}
}
function myMenuItem($compile) {
return {
template: '<a href ng-bind="item.name" ng-click="show($event)"></a>',
link: function(scope, element) {
if (angular.isArray(scope.item.menu)) {
element.append($compile('<ul ng-if="collapsed" my-menu="item.menu"></ul>')(scope));
}
scope.show = function($event) {
scope.collapsed = !scope.collapsed;
}
}
}
}
angular.module('app', [])
.controller('MainController', MainController)
.directive('myMenu', myMenu)
.directive('myMenuItem', myMenuItem);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.14/angular.js"></script>
<body ng-app="app" ng-controller="MainController">
<ul my-menu="menu">
</ul>
</body>
You need to distinguish between files and folders. Just treat the folders like you treat it in your example, and treat your files separately. The following snippet works, but I recommend that you take a minute to think about why and how it works.
function MainController($scope) {
$scope.menu = [{
name: 'one',
directoryList: [{
name: 'one'
}, {
name: 'two'
}],
fileList: ['one.pdf', 'two.pdf', 'three.pdf']
},
{
name: 'two',
directoryList: [{
name: 'one',
directoryList: [{
name: 'one'
}],
fileList: ['one.pdf', 'two.pdf', 'three.pdf', 'four.pdf']
}]
},
{
name: 'three'
}
];
}
function myMenu() {
return {
scope: {
myMenu: '=myMenu'
},
template: '<li ng-repeat="item in myMenu"><my-menu-item></my-menu-item></li>',
link: function(scope, elem) {}
}
}
function myMenuItem($compile) {
return {
template: `
<a href ng-bind="item.name" ng-click="show($event)"></a>
`,
link: function(scope, element) {
if (angular.isArray(scope.item.directoryList)) {
element.append($compile('<ul ng-if="!collapsed" my-menu="item.directoryList"></ul>')(scope));
}
if (angular.isArray(scope.item.fileList)) {
element.append($compile(`
<ul ng-if="item.fileList && !collapsed">
<li ng-repeat="file in item.fileList">{{file}}</li>
</ul>
`)(scope));
}
scope.collapsed = true;
scope.show = function($event) {
scope.collapsed = !scope.collapsed;
}
}
}
}
angular.module('app', [])
.controller('MainController', MainController)
.directive('myMenu', myMenu)
.directive('myMenuItem', myMenuItem);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.14/angular.js"></script>
<body ng-app="app" ng-controller="MainController">
<ul my-menu="menu">
</ul>
</body>

ng-click not working with Datatable and Angular Js

I have read several similar questions but none worked for me on this issue. I am using laravel-datatable with angular js. But 'ng-click' does not work with the dynamically generated datatable buttons. I read about $compile but dont know how to implement it with the table. Am very new to angular js.
When I click on the button nothing happens.
app.controller('myController', ['$scope','$compile', function($scope, $compile) {
$('#stafftbl').DataTable( {
paging: true,
"ordering": true,
"searching": true,
"bInfo" : true,
deferRender: true,
ajax: 'get_staffsalary',
columns: [
{ data: 'staff_num', name: 'staff_num'},
{ data: 'staffname ', name: 'staffname' },
{ data: 'salary', name: 'salary' },
{ data: 'date_effected', name: 'date_effected' },
{ data: 'updated_at', name: 'updated_at' },
{ data: null, render: function (data, type, full) {
return '<button class="btn btn-xs btn-primary" ng-click="update('+full.id+')">Update Salary</button> ';
}},
],
});
$scope.update= function (id) {
alert(id)
}
}]);
Please any help with this?
Yes, you are correct in that you need to use $compile.
After dynamically adding html with angular attributes in it, like you are with your data table, you must call $compile so that angular can pick up the attributes.
You'll need to inject the $compile service into your controller right after $scope. Then, after the HTML has been added you will need to compile the new DOM and link it to the scope by calling $compile(new_html)($scope) in the context of your controller.
Refer to the $compile doco for reference
I solved it using #MJL 's answer. Here is the full block of code, it may hep someone else
app.controller('myCtrl', ['$scope','$compile', function($scope, $compile) {
var table = $('#stafftbl').DataTable( {
processing: false,
serverSide: false,
deferRender: true,
ajax: 'get_staffsalary',
columns: [
{ data: 'staff_num', name: 'staff_num'},
{ data: 'salary', name: 'salary' },
{ data: 'date_effected', name: 'date_effected' },
{ data: 'updated_at', name: 'updated_at' },
{ data: null, render: function (data, type, full) {
return '<button class="btn btn-xs btn-primary text-size-small" ng-click="clickme()>Update</button> ';
}},
],
"createdRow": function ( row, data, index ) {
$compile(row)($scope); //add this to compile the DOM
}
})
$scope.clickme = function () {
alert('clicked me')
}
}]);

How to bind to a directive attribute?

I want the title for the highchart to be $scope.data.title but currently the attribute title interpret data.title as a string and a bind to the scope. I've tried putting "", {{}} around data.title the .html but it doesn't work. I think im missing something else.
index.html
<test-chart title="{{data.title}}">
<chart-series title="Series 1" type="line">
</chart-series>
<chart-series title="Series 3" type="column">
</chart-series>
</test-chart>
script.js
.directive('testChart', function() {
return {
restrict: 'E',
transclude: true,
controllerAs:'chartCtrl',
scope: {
},
controller: ['$scope', '$element', '$attrs', function ChartController($scope, $element, $attrs) {
$scope.data = {
title: 'HIGHGRAPH',
series: [{
title: 'series1',
type: 'line',
data: [1,2]
}, {
title: 'series2',
type: 'area',
data: [3,5]
}]
}
var hc = Highcharts.chart('highchart_container', {});
$scope.$watch("data",function(newValue,oldValue) {
hc.update({
title: {
text: newValue.title
}
})
})
this.addSeries = function(a) {
hc.addSeries({
name: a.title,
type: a.type,
data: [1,2,3,4,5,6]
})
};
}],
templateUrl: 'my-tabs.html'
};
EDIT: https://plnkr.co/edit/spUAkCjK61HgUGu40pZl?p=preview
This not works as intended, but is it possible to do it without the watch?
you won't have to put s.title inside ng-repeat. it should be outside.
Here is a working snippet.:-
var app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
});
app.directive('testChart', function() {
return {
restrict: 'E',
transclude: true,
controllerAs:'chartCtrl',
scope: {
},
controller: ['$scope', '$element', '$attrs', function ChartController($scope, $element, $attrs) {
$scope.data = {
title: 'HIGHGRAPH',
series: [{
title: 'series1',
type: 'line',
data: [1,2]
}, {
title: 'series2',
type: 'area',
data: [3,5]
}]
}
var hc = Highcharts.chart('highchart_container', {
title: {
text: $scope.data.title
}
});
this.addSeries = function(a) {
hc.addSeries({
name: a.title,
type: a.type,
data: [1,2,3,4,5,6]
})
};
}],
template: '<div> <p>This is a chart</p><ul> <li ng-repeat="s in series"> </li> </ul> <div id=\'highchart_container\'></div> <ng-transclude></ng-transclude> </div>'
};
})
.directive('chartSeries', function() {
return {
require: '^test-chart',
restrict: 'E',
transclude: true,
scope: {
title: '#'
},
link: function(scope, element, attrs, chartCtrl) {
chartCtrl.addSeries(attrs);
},
};
});
#highchart_container{
height:250px!important;
}
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<test-chart title="Custom title">
<chart-series title="Series 1" type="line">
</chart-series>
<chart-series title="Series 3" type="column">
</chart-series>
</test-chart>
</div>
Apparently this is how you must do it. Highchart docs for title
working plunker
Now my-tabs.html
<div>
<div id='highchart_container'></div>
<ng-transclude></ng-transclude>
</div>
and index.html
<body ng-app="docsTabsExample">
<test-chart>
<chart-series title="Series 1" type="line">
</chart-series>
<chart-series title="Series 3" type="column">
</chart-series>
</test-chart>
</body>
</html>
script.js
(function(angular) {
'use strict';
angular.module('docsTabsExample', [])
.directive('testChart', function() {
return {
restrict: 'E',
transclude: true,
controllerAs:'chartCtrl',
controller: ['$scope', '$element', '$attrs', function ChartController($scope, $element, $attrs) {
$scope.data = {
title: 'HIGHGRAPH',
series: [{
title: 'series1',
type: 'line',
data: [1,2]
}, {
title: 'series2',
type: 'area',
data: [3,5]
}]
}
var hc = Highcharts.chart('highchart_container', {
title: {
align:"center",
text:$scope.data.title
}
});
this.addSeries = function(a) {
hc.addSeries({
name: a.title,
type: a.type,
data: [1,2,3,4,5,6]
})
};
}],
templateUrl: 'my-tabs.html'
};
})
.directive('chartSeries', function() {
return {
require: '^test-chart',
restrict: 'E',
transclude: true,
scope: {
title: '#'
},
link: function(scope, element, attrs, chartCtrl) {
chartCtrl.addSeries(attrs);
},
};
});
})(window.angular);
i can't see why you need data binding if the title is a scope variable of the chartCtrl.
this will simply do the trick:
<div>
{{data.title}}
<div id='highchart_container'></div>
<ng-transclude></ng-transclude>
</div>
Plunker
Solved it by adding a watch on the object.
https://plnkr.co/edit/spUAkCjK61HgUGu40pZl?p=preview

angular dropdown multiselect to connect data between three dropdowns

I have three multiselect dropdowns:-
<label>Dropdown One</label>
<div ng-model="a.dp1" ng-dropdown-multiselect="" options="multiSelectArray" selected-model="dropDownOne" extra-settings="multiSelectSettings">
</div>
<label>Dropdown Two</label>
<div ng-model="a.dp2" ng-dropdown-multiselect="" options="multiSelectArray" selected-model="dropDownTwo" extra-settings="multiSelectSettings">
</div>
<label>Dropdown Three</label>
<div ng-model="a.dp3" ng-dropdown-multiselect="" options="multiSelectArray" selected-model="dropDownThree" extra-settings="multiSelectSettings">
</div>
Directive Code:-
(function () {
'use strict';
angular.module('myApp.components')
.directive('page', page);
page.$inject = ['$http', '$timeout', 'ApiServices'];
function page($http, $timeout, ApiServices) {
return {
restrict: 'EA',
scope: {},
controller: function ($scope) {
$scope.a = { };
$scope.dropDownOne = [];
$scope.dropDownTwo = [];
$scope.dropDownThree = [];
$scope.multiSelectArray = [{
name: "Ayan"
}, {
name: "Rita"
}, {
name: "Mohit"
}, {
name: "Shittal"
}, {
name: "Jayant"
}, {
name: "Sachin"
}, {
name: "Tina"
}, {
name: "Babita"
}, {
name: "Priya"
}];
$scope.multiSelectSettings = {
smartButtonMaxItems: 11,
scrollable: true,
displayProp: "name",
idProp: "name",
externalIdProp: "name"
};
},
templateUrl: 'js/folder/system/page.html'
};
}
})();
What I am trying to do here is when I select particular options from 'Dropdown One' the same options got selected in 'Dropdown Two' and 'Dropdown Three' and get disabled so that users can't unselect them. Also, the users can select more options in 'Dropdown Two' and 'Dropdown Three' if they want, but the options from 'Dropdown One' should be checked already and disabled.
I am trying to disable the options using 'disabled' attribute but not able to for selected options. Any idea how I can do that?

Pass locals in mdBottomSheets(angular material bottom sheets)

I have tried to pass locals in BottomSheet Controller
//Bottom Sheet Controller
angular
.module('app').controller('BottomSheetCtrl', function($scope, $mdBottomSheet) {
$scope.items = [
{ name: 'Share', icon: 'share-arrow' },
{ name: 'Upload', icon: 'upload' },
{ name: 'Copy', icon: 'copy' },
];
$scope.items.append($scope.Item);
console.log($scope.items);
});
//AppCtrl
angular
.module('app').controller('AppCtrl', function($scope, $mdBottomSheet){
$scope.openBottomSheet = function() {
$mdBottomSheet.show({
template:
'<md-bottom-sheet>{{}}</md-bottom-sheet>',
controller: 'BottomSheetCtrl',
scope: $scope.$new(true),
// preserveScope: true,
locals: {
Item: {
'name': 'Print this page', 'icon': 'print'
},
}
});
};
});
But $scope.Item is not populating. What is the correct way of passing locals in BottomSheet Controller?
You have to inject the locals into the bottom sheet controller - CodePen
Markup
<div ng-controller="BottomSheetExample" class="md-padding bottomSheetdemoBasicUsage" ng-cloak="" ng-app="MyApp">
<md-button flex="50" class="md-primary md-raised" ng-click="showListBottomSheet()">Show as List</md-button>
</div>
JS
angular.module('MyApp',['ngMaterial', 'ngMessages', 'material.svgAssetsCache'])
.controller('BottomSheetExample', function($scope, $timeout, $mdBottomSheet, $mdToast) {
$scope.showListBottomSheet = function() {
$scope.alert = '';
$mdBottomSheet.show({
template: '<md-bottom-sheet ng-cloak>{{Item.name}}</md-bottom-sheet>',
controller: 'ListBottomSheetCtrl',
locals: {
Item: {
'name': 'Print this page', 'icon': 'print'
},
}
}).then(function(clickedItem) {
$scope.alert = clickedItem['name'] + ' clicked!';
});
};
})
.controller('ListBottomSheetCtrl', function($scope, $mdBottomSheet, Item) {
console.log(Item);
$scope.Item = Item;
});
Bottom sheet reference
locals - {string=}: An object containing key/value pairs. The keys
will be used as names of values to inject into the controller. For
example, locals: {three: 3} would inject three into the controller
with the value of 3.
As an alternative you can also set the bindToController property to true. This allows accessing locals in the BottomSheetCtrl. So in the BottomSheetCtrl you can then get the value of Item like this:
var Item = this.locals.Item;
JS
//Bottom Sheet Controller
angular
.module('app').controller('BottomSheetCtrl', function($scope,
$mdBottomSheet) {
$scope.items = [
{ name: 'Share', icon: 'share-arrow' },
{ name: 'Upload', icon: 'upload' },
{ name: 'Copy', icon: 'copy' },
];
var Item = this.locals.Item;
$scope.items.append(Item);
console.log($scope.items);
});
//AppCtrl
angular
.module('app').controller('AppCtrl', function($scope, $mdBottomSheet){
$scope.openBottomSheet = function() {
$mdBottomSheet.show({
template:
'<md-bottom-sheet>{{}}</md-bottom-sheet>',
controller: 'BottomSheetCtrl',
scope: $scope.$new(true),
bindToController: true,
locals: {
Item: {
'name': 'Print this page', 'icon': 'print'
},
}
});
};
});

Resources