ng-class using classes from variable under condition - angularjs

I am using a directive and want to use ng-class in a way that I could output multiple classes (which are stored in a variable) under certain condition.
<div ng-if="showIcons" ng-class="{state.icon: showIcons}"></div>
$scope.state = {
icon: 'icon-cross-outline color-brand-danger'
};
Unfortunately this is not working.

Using ternary operator in ng-class, could you try the following?
<div ng-if="showIcons" ng-class="showIcons ? state.icon : null"></div>
Demo on Plunker

You can do more complicated stuff via binding a function to the ng-class see my plnker
JS:
var app = angular.module('hey', []);
app.controller('TheController', [function () {
this.title = "hey"
this.shouldApply = true;
this.canApplyClass = function () {
if (this.shouldApply == true) {
return 'happy sad other';
}
return '';
}
}]);
HTML:
<body ng-app="hey">
<div ng-controller="TheController as c">
<h1 ng-class="c.canApplyClass()">{{c.title}}</h1>
</div>
</body>
I prefer this approach because you can get complicated in the function - and even have access to other services.

Related

Function of isolated scope gets called multiple times

I wrote an angular code to implement Like and remove Like functionality. For this purpose I used a directive "like-directive" which takes three functions
1. upFn : code to be executed when liked
2. downFn: code to be executed when like removed
3. upvChk: function which checks whether it is liked or not. I started this with false
For some reason I cannot use a variable with ng-if. I have to use the upvChk function. But this function is getting called more than twice. It should be called only twice because I'm using ng-if twice.
Here is the codepen link http://codepen.io/puneet27/pen/jApJww/
angular.module('app',[])
.controller('MainCtrl',['$scope',MainCtrl])
.directive('likeDirective',[likeDirective]);
function MainCtrl($scope){
var mcl = this;
mcl.checkUpvoted = checkUpvoted;
mcl.upvote = upvote;
mcl.downvote = downvote;
function checkUpvoted(){
alert("function to check whether liked or not");
return mcl.upvoted;
}
function upvote(){
alert('like completed');
mcl.upvoted = true;
}
function downvote(){
alert('dislike completed');
mcl.upvoted = false;
}
}
function likeDirective(){
return {
restrict: 'E',
scope: {
upFn:'&upFn',
downFn:'&downFn',
upvChk:'&upvChk'
},
templateUrl: 'tpl.html'
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="MainCtrl as mc">
<like-directive up-fn="mc.upvote()" down-fn="mc.downvote()" upv-chk="mc.checkUpvoted()"></like-directive>
</div>
<script type="text/ng-template" id="tpl.html">
<div><span id="upvote" ng-if="!upvChk()" ng-click="upFn()">Like</span><span id="upvoted" ng-if="upvChk()" ng-click="downFn()">Liked</span></div>
</script>
</div>

AngularJS factory inside ng-class on body tag not working

Was told to create service/factory and use it on global controller. It's my first time hearing global controller. Anyways, I created a factory called Scroll and injected it on our main.controller.js. The function in the factory is isScrollingEnabled which returns true or false. The other function is setScrolling which will set the variable to true or false. The default value is set to true.
Inside the main controller, I have this code
$scope.scrollEnabled = Scroll.isScrollingEnabled();
console.log('value of $scope.scrollEnabled', $scope.scrollEnabled);
That code spits out true in the console which is good.
on my template, I'm using it this way. I have button that sets scrolling to false. The value in the console spits out false which is good.
<body ng-class="{ 'scrolling' : scrollEnabled }">
However, it's not working. If I change it to the code written below, it works
<body ng-class="{ 'scrolling' : false }">
So I guess, it's not in scope especially ui-view is in index.html and main.controller.js and main.html will be loaded in the ui-view. The < body > is before this which tells me, any scope inside main.controller.js will not work outside of ui-view.
So what's the solution for this?
Sorry for not posting the factory. Here it is
.factory('Scroll', function() {
var scrollEnabled = true; // i then changed it to false hoping it will work, it didn't
var ScrollEvent = {
isScrollingEnabled: function () {
return scrollEnabled;
},
disablePageScrolling: function() {
scrollEnabled = false;
}
};
return ScrollEvent;
});
The $scope of the controller you're attaching the value to doesn't extend to the <body> element. Instead, you can whip together a directive:
.directive('shouldScroll', function (Scroll) {
return {
restrict: 'A',
link: function ($scope, elem) {
$scope.$watch(Scroll.isScrollingEnabled, function (n) {
if (n) {
elem.addClass('scrolling');
} else if (n === false) {
elem.removeClass('scrolling');
}
});
}
};
});
You'd attach it to body like so:
<body should-scroll>
Another solution that works in some cases is just to use class rather than ng-class:
<body class="{{ scrollEnabled ? 'scrolling' : '' }}">

Scope values to a requested content

I have a view that contains a button, when the button is clicked, a $http.get request is executed and the content is appended on the view.
View:
<button ng-click="includeContent()">Include</button>
<div id="container"></div>
Controller:
$scope.includeContent = function() {
$http.get('url').success(function(data) {
document.getElementById('container').innerHTML = data;
}
}
The content to include:
<h1>Hey, I would like to be {{ object }}</h1>
How can I scope a value to object? Do I need to approach this in a complete different way?
The built-in directive ng-bind-html is the way you are looking for.
Beware, that ng-bind-html requires a sanitized string, which is either done automatically when the correct libary is found or it can be done manually ($sce.trustAsHtml).
Don't forget to inject $sce in your controller.
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.data = $sce.trustAsHtml(data);
}
}
<button ng-click="includeContent()">Include</button>
<div ng-bind-html="data"></div>
As you also want to interpolate your requested HTML, I suggest using $interpolate or, if it can contain whole directives or should have a full fledged two-way-data-binding, use $compile instead.
In your case alter the assignment to
$scope.data = $sce.trustAsHtml($interpolate(data)($scope));
Don't forget to inject $interpolate/$compile aswell.
As I don't know about your $scope structure I assume that "object" is available in this scope. If this isn't the case then change the $scope parameter to whatever object contains your interpolation data.
You should use a controller to do this (I imagine you are since you're using $scope).
ctrl function () {
var ctrl = this;
ctrl.includeContent = function () {
$http.get("url").success(function (data) {
ctrl.object = data;
});
};
}
<div ng-controller="ctrl as ctrl">
<button ng-click="ctrl.includeContent()">Include</button>
<div id="container">
<h1 ng-show="ctrl.object">Hey, I would like to be {{ctrl.object}}</h1>
</div>
</div>
You need not select an element and append the data to it. Angular does it for you. That's what is magic about angular.
In your controller's scope, just update object and angular does the heavy-lifting
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.object = data;
}
}
If that's html code from a server, then you should use the 'ng-bind-html' attribute:
<button ng-click="includeContent()">Include</button>
<div id="container" ng-bind-html="htmlModel.ajaxData"></div>
Controller:
$scope.htmlModel = {ajaxData:''};
$scope.includeContent = function() {
$http.get('url').success(function(data) {
$scope.htmlModel.ajaxDataL = data;
}
}
One way is to use ng-bind-html as suggested.
Another way is with $compile:
app.controller('MainCtrl', function($scope, $http, $compile) {
$scope.error='error!!!';
$scope.includeContent = function() {
$http.get('url').success(function(data) {
var elm = angular.element(document.getElementById('container')).html(data);
$compile(elm)($scope);
}).error(function(){
var elm = angular.element(document.getElementById('container')).html('{{error}}');
$compile(elm)($scope);
})
}
});
Also, typically in angular, when you want to manipulate the DOM you use directives.
DEMO

Angular : ng-if with dictionaries

Kind new to AngularJS I'm encountering the following problem :
How could eval with ng-if if a dictionary is empty or not ?
With arrays ng-if="myArray.length" works great but doesn't with dictionaries.
Edit : Also already tried Object.keys(myDict).length which doesn't work.
ng-if is also able to evaluate a function in scope.
For example:
<div ng-if="functionHere(x)" ></div>
Then in your controller, you could have
$scope.functionHere = function(input) { if [logic here]..... }
So, if you could contain your logic in a javascript function, then you could delegate the ng-if's decision to what the function returns.
JSFiiddle Example
Use a scope function:
html:
<div ng-app="myModule" ng-controller="myController">
<div ng-if="!isEmptyObject(myObj)">
<h1>Hello</h1>
</div>
</div>
javascript:
angular.module('myModule', [])
.controller('myController', ['$scope', function($scope) {
$scope.isEmptyObject = function(obj) {
for(var prop in obj) {
if(obj.hasOwnProperty(prop))
return false;
}
return true;
}
$scope.myObj = {};
}]);
Check out this fiddle

AngularJS - How to show custom directives based on condition

I have to show a custom directive (i.e. task-moveable) based on some condition. I have to only show the task-movable attribute for tasks which is not completed yet. Is there any way we can do this in Angularjs?
HTML Code:
<div class="gantt-body-background" ng-repeat="row in gantt.rows" task-moveable>
....
</div>
Thanks in advance.
You could make a tweak such that your taskMoveable directive can observe a value assigned to it. From there do an $eval on the value of the taskMoveable attribute to get your boolean.
As an example:
app.directive('taskMoveable', function () {
return {
controller: function ($scope, $element, $attrs) {
$scope.taskMoveable = {};
$attrs.$observe('taskMoveable', function (value) {
if (value) {
$scope.taskMoveable.amIMoveable = $scope.$eval(value);
}
});
},
template: '<span ng-bind="taskMoveable.amIMoveable"></span>'
};
});
See my plunk here for a more detailed example:
http://plnkr.co/edit/0nK4K9j3SmNnz8PgRYfR
You could use ng-if for that whole element. Something like this.
<div class="gantt-body-background" ng-repeat="row in gantt.rows" ng-if="thing.stuff" task-moveable>
....
</div>
Then that div would only be in the DOM if thing.stuff was truthy.

Resources