ng-class is not updated even when interpolated value change to false - javascript

I have this directive:
import 'jquery';
import 'jquery-ui/ui/widgets/slider';
import 'jquery-ui/themes/base/core.css';
import 'jquery-ui/themes/base/slider.css';
import 'jquery-ui/themes/base/theme.css';
import './range.css';
function range() {
return {
restrict: 'E',
scope: {
min: '#',
max: '#',
startLabel: '&',
endLabel: '&',
showLabels: '#'
},
require: 'ngModel',
template: '<div><div>{{showLabels}}</div><div class="range" ng-class="{labels: showLabels}"></div></div>',
link: function($scope, $element, $attrs, ngModelController) {
var $range = $($element).find('.range');
$scope.min = $scope.min || 0;
$scope.max = $scope.max || 100;
function updateCustomProperties(range) {
$range[0].style.setProperty("--from", (range[0] / $scope.max) * 100);
$range[0].style.setProperty("--to", (range[1] / $scope.max) * 100);
}
if (!ngModelController.$modelValue) {
ngModelController.$modelValue = [$scope.min, $scope.max]
}
$scope.$watch('startLabel', function(value) {
$range.find('.ui-slider-handle').eq(0).attr('data-value', value);
});
$scope.$watch('endLabel', function(value) {
$range.find('.ui-slider-handle').eq(1).attr('data-value', value);
});
$range.slider({
min: $scope.min,
max: $scope.max,
step: 1,
values: ngModelController.$modelValue,
slide: function(event, ui) {
function updateModel() {
ngModelController.$setViewValue(ui.values);
}
if (!$scope.$$phase) {
$scope.$apply(updateModel);
} else {
updateModel();
}
updateCustomProperties(ui.values);
}
});
updateCustomProperties(ngModelController.$modelValue);
$scope.$watch('min', function(value) {
$range.slider('option', 'min', value);
});
$scope.$watch('max', function(value, oldValue) {
$range.slider('option', 'max', value);
updateCustomProperties(ngModelController.$modelValue);
});
ngModelController.$render = function() {
if (ngModelController.$modelValue) {
$range.slider('option', 'values', ngModelController.$modelValue);
updateCustomProperties(ngModelController.$modelValue);
}
};
}
};
}
//range.$inject = [];
module.exports = range;
and this usage:
<range start-label="vm.monthYear(vm.DashboardService.fromDate)"
end-label="vm.monthYear(vm.DashboardService.toDate)"
show-labels="{{vm.MonthDiff(vm.DashboardService.fromDate, vm.DashboardService.toDate) > 2}}"
max="{{vm.range.max}}"
class="slider" ng-model="vm.range.values"></range>
when the value of show-labels change to false I get the value in directive template ({{showLabels}} is false) but the class is not removed, it's always on the DOM element when I inspect it in developer tools.
Why the class is to removed?

The interpolated value is not parsed it's a string to fix it you can call JSON.parse on the interpolated value:
function range() {
return {
restrict: 'E',
scope: {
min: '#',
max: '#',
startLabel: '&',
endLabel: '&',
showLabels: '#'
},
require: 'ngModel',
template: '<div class="range" ng-class="{labels: labels}"></div>',
link: function($scope, $element, $attrs, ngModelController) {
var $range = $($element).find('.range');
$scope.min = $scope.min || 0;
$scope.max = $scope.max || 100;
$scope.$watch('showLabels', (value) => {
$scope.labels = JSON.parse(value);
});
...
}
};
}

Related

AngularJS recursive template close by default all nodes and expand them with ng-click

is it possible with this example from Ben Foster to get all nodes closed by default (at the loading of the page) ? and to open each node with ng-click ?
http://jsfiddle.net/benfosterdev/NP7P5/
I have found a way to get selected node but I don't know how to combine it with ng-click and eventually ng-show or ng-hide:
ng-click='nodeSelected($event, category)'
and in controller
$scope.nodeSelected = function($event, category){
$event.stopPropagation();
console.log('This node is selected' + category);
}
Just found a similar example :
var gyroEditor = angular.module('gyroEditor', []);
gyroEditor.controller('Ctrl', function($scope) {
$scope.nodes = [
{
title: 'Computers',
categories: [
{
title: 'Laptops',
categories: [
{
title: 'Ultrabooks'
},
{
title: 'Macbooks'
}
]
},
{
title: 'Desktops'
},
{
title: 'Tablets',
categories: [
{
title: 'Apple'
},
{
title: 'Android'
}
]
}
]
},
{
title: 'Printers'
}
];
});
gyroEditor.directive('tree', function() {
return {
restrict: 'E',
replace: true,
scope: {nodes: '=nodes'},
templateUrl: 'tree.html',
controller: function($scope) {
console.log('tree ctrl');
}
};
});
gyroEditor.directive('treenode', function() {
return {
restrict: 'E',
replace: true,
scope: {node:'=node'},
templateUrl: 'treenode.html',
controller: function($scope) {
console.log('node ctrl');
}
};
});
gyroEditor.directive("recursive", function($compile) {
return {
restrict: "EACM",
priority: 100000,
compile: function(tElement, tAttr) {
var contents = tElement.contents().remove();
var compiledContents;
return function(scope, iElement, iAttr) {
if(!compiledContents) {
compiledContents = $compile(contents);
}
iElement.append(
compiledContents(scope,
function(clone) {
return clone; }));
};
}
};
});
.panel-left {
float: left;
width: 200px;
margin: 0 20px 20px 0;
}
.panel-editors {
float: right;
height: 100%;
width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app=gyroEditor ng-controller=Ctrl>
<script type="text/ng-template" id="treenode.html">
<li ng-init="collapsed=true">
<a ng-click="collapsed=!collapsed"><i class="fa fa-{{((collapsed || !node.categories) ? '' : '-open')}}"></i> {{node.title}}</a>
<ol ng-if="!collapsed && node.categories && node.categories.length">
<recursive>
<treenode ng-repeat="c in node.categories" node=c>
</treenode>
</recursive>
</ol>
</li>
</script>
<script type="text/ng-template" id="tree.html">
<ol>
<treenode ng-repeat="n in nodes" node=n></treenode>
</ol>
</script>
<div class=panel-left>
<tree nodes=nodes></tree>
</div>
</div>
{{node.title}}

How to handle click event in angular1 directive test cases

I am trying to write test case for my directive in anguarjs1.x
here is my directive
.directive("weeklyDirective", function($timeout) {
return {
scope: {
data: '=',
},
link: function(scope, element) {
scope.weekDays = [
{ text: 'Sun', id: 1 },
{ text: 'Mon', id: 2 },
{ text: 'Tue', id: 3 },
{ text: 'Wed', id: 4 },
{ text: 'Thu', id: 5 },
{ text: 'Fri', id: 6 },
{ text: 'Sat', id: 7 }
];
},
restrict: 'A',
templateUrl: "/flat-ui/tpls/weekly-scheduler.html",
};
})
where is my directive template
<ul style="padding: 0px;display: inline;margin: 0;list-style: none;">
<li ng-repeat="weekDay in weekDays" style="padding: 10px;display: inline;">
<input type="checkbox" value="{{weekDay.id}}" check-list="data.weeklyDetails" id="{{'chk_'+$index}}" class="ee-check"> <label class="ee-check" for="{{'chk_'+$index}}"><span></span> {{weekDay.text}}</label>
</li>
</ul>
In my weekly directive, I have used another directive which handles my checkbox list
.directive('checkList', function() {
return {
scope: {
list: '=checkList',
value: '#'
},
link: function(scope, elem, attrs) {
var handler = function(setup) {
var checked = elem.prop('checked');
var index = scope.list.indexOf(scope.value);
if (checked && index == -1) {
if (setup) elem.prop('checked', false);
else scope.list.push(scope.value);
} else if (!checked && index != -1) {
if (setup) elem.prop('checked', true);
else scope.list.splice(index, 1);
}
};
var setupHandler = handler.bind(null, true);
var changeHandler = handler.bind(null, false);
elem.bind('change', function() {
scope.$apply(changeHandler);
});
scope.$watch('list', setupHandler, true);
}
};
});
now I am trying to write test cases to test my directive
describe("weeklyDirective directive", function() {
var elm, scope, httpBackend, controller;
beforeEach(module('guideApp.directives'));
beforeEach(module('/flat-ui/tpls/weekly-scheduler.html'));
beforeEach(angular.mock.inject(function($rootScope, $compile) {
compile = $compile;
scope = $rootScope;
elm = angular.element('<div weekly-directive data="data"></div>');
compile(elm)(scope);
scope.data = {
interval: 1,
weeklyDetails: ['1'],
}
scope.$digest();
}));
it("click on check box it should get added in weeklyDetails", function() {
var e = elm.find('input[id="chk_3"]');
console.log(e);
e.trigger('click');
scope.$apply();
var isolateScope = elm.isolateScope();
expect(isolateScope.data.weeklyDetails.indexOf('4') > -1).toBeTruthy();
});
});
where I am trying to test that when user click on check box its value should get added to my array weeklyDetails which in data object (passed to the weeklydirective).
its not working as exptected for me please help me to get this working.
Thanks in Adv.

Angular $interval impact if set invokeApply to false

I got a directive something like below:
app.directive('testing', ['$interval', function($interval) {
return {
restrict:'E',
scope: {
data: '='
},
link: function(scope, element) {
var myInterval = $interval(function() {
scope.testData = true;
}, 1000, 0, false);
},
templateUrl: 'view/test.html'
};
}]);
In my view/test.html
<span ng-if="testData">Showing testData</span>
Initially I thought setting invokeApply to false, ng-if will not be fired.
var myInterval = $interval(function() {
scope.testData = true;
}, 1000, 0, false);
But regardless of true / false
var myInterval = $interval(function() {
scope.testData = true;
}, 1000, 0, true);
scope.testData will still be watched by ng-if.
So my question is, when I set invokeApply to false, what will be the main differences or impact to my app?

Directive within a directive within a template not working

I'm an Angular noob. I'm structuring my panes as sets of tables and using directives to keep the template HTML abstract from the table implementation. The panes are getting transcluded correctly, but interior only contains the text not the table structure. Here's my code:
<!DOCTYPE html>
<html>
<head>
<script src= "angular.js"></script>
<script src= "angular-sanitize.js"></script>
</head>
<body ng-app="pp">
<div ng-controller="ppMain">
<ul class="menu">
<li ng-repeat="pageID in homePageList">{{pages[pageID].str}}</li>
</ul>
<div>
<div ng-repeat="pageID in pageList" ng-bind-html="pages[pageID].template">
</div>
</div>
</div>
<script>
var pp = {
ctl: {}
};
pp.mod = angular.module('pp', ['ngSanitize']);
pp.mod.directive({
'ppHeader': function () {
return ({
});
},
'ppGroup': function () {
return ({
template: '<table ng-transclude></table>',
transclude: true,
restrict: 'EA'
});
},
'ppRow': function () {
return ({
template: '<tr ng-transclude></tr>',
transclude: true,
restrict: 'EA'
});
},
'ppLabel': function () {
return ({
template: '<td ng-transclude></td>',
transclude: true,
restrict: 'EA'
});
},
'ppValue': function () {
return ({
template: '<td ng-transclude></td>',
transclude: true,
restrict: 'EA'
});
},
});
pp.ctl.main = pp.mod.controller('ppMain', ['$scope', function ($scope) {
$scope.curPage = 'page1';
$scope.pages = {
"page1": {
str: "page 1",
template:'\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>1</pp-value>\
</pp-row>\
</pp-group>\
'},
"page2": {
str: "page 2",
template:'\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>2</pp-value>\
</pp-row>\
</pp-group>\
'},
"page3": {
str: "page 3",
template:'\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>3</pp-value>\
</pp-row>\
</pp-group>\
'}
};
$scope.pageList = ["page1","page2","page3"];
$scope.homePageList = ["page2", "page3"];
}]);
</script>
</body>
</html>
Looking at the generated DOM with the debugger, I find no tables, only text.
That is not possible with ng-bind-html because ng-sanitize does not compile the html. It can only be used for static content. You would need to create a directive yourself for this.
Something like this:
.directive('compileHtml', function($compile) {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
var template = scope.$eval(attrs.compileHtml); //get the template by evaluating
elm.html(template); //set the html
$compile(elm.contents())(scope); //compile the contents
}
}
});
Also remember that you have child directives with no replace, which means you will be creating invalid html, table created by ppGroup will have ppRow as child which results in invalid html and it will push the directive content out of the table and compilation will not happen properly. So you would instead need to use the option replace:true on these directives.
However it will be really unsafe to compile the html dynamically though unless you know it is from a trusted source which is what $sce service used by ng-bind-html does. It sanitizes the boundhtml.
Demo
var pp = {
ctl: {}
};
pp.mod = angular.module('pp', ['ngSanitize']);
pp.mod.directive({
'ppHeader': function() {
return ({});
},
'ppGroup': function() {
return ({
template: '<table ng-transclude></table>',
transclude: true,
restrict: 'EA'
});
},
'ppRow': function() {
return ({
replace: true,
template: '<tr ng-transclude></tr>',
transclude: true,
restrict: 'EA'
});
},
'ppLabel': function() {
return ({
replace: true,
template: '<td ng-transclude></td>',
transclude: true,
restrict: 'EA'
});
},
'ppValue': function() {
return ({
replace: true,
template: '<td ng-transclude></td>',
transclude: true,
restrict: 'EA'
});
},
}).directive('compileHtml', function($compile) {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
var template = scope.$eval(attrs.compileHtml);
elm.html(template);
$compile(elm.contents())(scope);
}
}
});
pp.ctl.main = pp.mod.controller('ppMain', ['$scope',
function($scope) {
$scope.curPage = 'page1';
$scope.pages = {
"page1": {
str: "page 1",
template: '\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>1</pp-value>\
</pp-row>\
</pp-group>\
'
},
"page2": {
str: "page 2",
template: '\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>2</pp-value>\
</pp-row>\
</pp-group>\
'
},
"page3": {
str: "page 3",
template: '\
<pp-group>\
<pp-row>\
<pp-label>Page:</pp-label><pp-value>3</pp-value>\
</pp-row>\
</pp-group>\
'
}
};
$scope.pageList = ["page1", "page2", "page3"];
$scope.homePageList = ["page2", "page3"];
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.3/angular-sanitize.min.js"></script>
<div ng-app="pp">
<div ng-controller="ppMain">
<ul class="menu">
<li ng-repeat="pageID in homePageList">{{pages[pageID].str}}</li>
</ul>
<div>
<div ng-repeat="pageID in pageList" compile-html="pages[pageID].template">
</div>
</div>
</div>
</div>

Passing an object to a directive as an argument

I have a view and a controller for it:
<app-head options="options"></app-head>
app.controller('homeCtrl',function($scope){
$scope.options = {
first: {
description: "A link",
link: "../"
},
second:{
description: "Another link",
link: "../../"
}
};
});
I want to use the object options within my directive:
app.directive('appHead',function(){
return{
restrict: 'E',
templateUrl: 'partials/modules/appHead.html',
replace: true,
controller: function($scope,$element,$attrs){
$scope.options = $attrs.options;
}
}
});
When i call console.log($scope.options) in my directive's controller however, it will return options
No Properties
Why is that and how do i accomplish this?
Try this
app.directive('appHead',function(){
return{
scope: {
options: '=',
},
restrict: 'E',
templateUrl: 'partials/modules/appHead.html',
replace: true,
link: function($scope,$element,$attrs){
// $scope.options
}
}
});
Working Demo

Categories

Resources