Conditional logic in compile or link function of angular js directive? - angularjs

I have a JSON object in $scope called data. The object has a member called items which may either be an Object or Array of Objects. For this reason I can't use ng-repeat:
<ul>
<li ng-repeat="item in data.items">
<item data="item"></item>
</li>
</ul>
...because if data.items is an object it creates an <item> directive for each member in data.items. However things work fine if data.items is an array. I thought I could do something like:
<span ng-if="angular.isObject(data.items)">
<span ng-if="angular.isDefined(data.items)">
<item data="data.item"></item>
</span>
</span>
But this doesn't work, I guess because I need the argument to isObject and isDefined to be an expression that can be resolved, but wrapping the argument in {{}}} just results in a syntax error.
I then thought I could create a directive that would inspect items and modify the template element in the compile function, however this requires access to data on $scope, which compile functions don't have. I think the linking function would be too late to modify the template?

You can do something like this:
<ul ng-if="data.items.length>0">
<li ng-repeat="item in data.items">
<item data="item"></item>
</li>
</ul>
<span ng-if="data.items && !(data.items.length>0)">
<item data="data.items"></item>
</span>

Related

Expression in ng-if of AngularJS

Consider the following snippet
ng-if not working
<div class="row">
<div class="col-md-8">
<ul>
<li ng-repeat="bigL in bigLs">
<span ng-if="isObj(bigL)">{{bigL.note}}</span>
<ul ng-if="bigL instanceof Array">
<li ng-repeat="bigLl in bigL">
{{bigLl}}
</li>
</ul>
</li>
</ul>
</div>
</div>
ng-if working
<div class="row">
<div class="col-md-8">
<ul>
<li ng-repeat="bigL in bigLs">
<span ng-if="isObj(bigL)">{{bigL.note}}</span>
<ul ng-if="isArr(bigL)">
<li ng-repeat="bigLl in bigL">
{{bigLl}}
</li>
</ul>
</li>
</ul>
</div>
</div>
Controller
$scope.isArr = function(bigL){
return bigL instanceof Array;
};
I use ng-if to determine whether a nested ul is required to create (by determinate different data type inside the array bigLs), I come to a situation that ng-if cannot evaluate bigL instanceof Array, I then move this snippet inside a function, with the same context, the ng-of works properly, but still cannot understand why it is a need to wrap the expression inside a function instead of running it directly inside the ng-if.
Appreciate for any clarification, thanks!
I'm not exactly sure of the problem, but there are several things that have bad smells in your code:
Don't use 'instanceof Array', ever. It won't work in an angular expression.
Instead, use angular.isArray(). This will only work in javascript by adding a method to your scope.
So, you would want to do something like this:
Controller:
...
$scope.hasChildren = function(bigL1) {
return angular.isArray(bigL1);
}
Template:
...
<ul ng-if="hasChildren(bigL)">
...
As a bonus, it becomes much easier to unit test this code.

How to ng-repeat over array with arrays as elements?

I have a array like this:
$scope.sortable = [["inProgressCounter","31"],["approvedCounter","3"],["pendingCounter","35"],["rejectedCounter","0"]]
I need to show them with ng-repeat my code is:
<ul>
<li ng-repeat="info in sortable">{{info[0]}}{{info[1]}}</li>
</ul>
but it gives me nothing.
so I also tried print the data out in html like this:
{{sortable}}
<ul>
<li ng-repeat="info in sortable">{{info[0]}}{{info[1]}}</li>
</ul>
it works,and shows me the data.
Here is a plunker of this working. This was all using the code you provided.
There must be something wrong with your controller, or you arent even assigning a controller to anything.
code
http://plnkr.co/edit/4pEsP6mr0tA6Toe9coTG?p=preview

Submit and disable all buttons in an ng-repeat without adding a new property on the scope

I have a list of items where each item is clickable and should trigger the submit(id) function. I would like to disable all items (buttons) when one of them is clicked.
<ul>
<li ng-repeat="item in items">
<button ng-click="submit(item.id)">Submit</button>
</li>
</ul>
I could define $scope.submitted variable in a controller and then set ng-disabled="submitted" in my view. I could also wrap it into a <form> and use frm.$submitted. Well... I would like to define everything inside view.
I search for an elegant solution where a don't have to define a $scope variable to achieve this. What do you propose?
If you do not want to define a property in the controller, and assuming that submit function never fails to submit. You could utilize the items array itself, by adding a property when click happens and after submit function is run.
<ul>
<li ng-repeat="item in items">
<button ng-click="submit(item.id); items.submitted=true"
ng-disabled="items.submitted">Submit</button>
</li>
</ul>
With this you are adding a property in the array items (so that it is available across child scopes of ng-repeat as well as items is set on its parent scope) so essentially you are not defining a new scope variable for this.
angular.module('app',[]).controller('ctrl', function($scope){
$scope.items = [{id:1},{id:2},{id:3},{id:4}]
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<ul>
<li ng-repeat="item in items">
<button ng-click="submit(item.id); items.submitted=true"
ng-disabled="items.submitted">Submit</button>
</li>
</ul>
</div>

Uncertainty about the use of ngInclude

I render recursive template with ngInclude. Here is my code:
<!--
definitionsRenderer.html
-->
<ul data-ng-if="currentLoc.definitions && templateIsloaded">
<li ng-repeat="definition in currentLoc.definitions">
{{definition.text}}
</li>
</ul>
<ul data-ng-if="templateIsloaded">
<li ng-repeat="loc in currentLoc.loc" ng-include="'./partials/definitionsRenderer.html'"></li>
</ul>
I need code to be executed asynchronous. So for scope of every included template
I want to have a variable - templateIsloaded with value false by default. When template is loaded and rendered it value to be true. How do that?
Best regards.
To have separate $scope for each of recursive called includes you can simply add ng-controller.
<li ng-repeat="loc in currentLoc.loc" ng-controller="definitionsRendererCtrl" ng-include="'./partials/definitionsRenderer.html'"></li>

Setting ng-click in ng-repeat dynamically

I have a lot of data within ng-repeat. It looks like table with many rows and columns. I want to put ng-click directive on some of cell of table.
Can i put some condition before ng-click directive? If this condition is true - I want to put the directive, otherwise - not to put.
I think you should just put the directive in there and pass it a condition as a parameter which returns boolean. In the directive compile function check if your parameter is right and decide from there what you load.
Maybe this is what you were looking for?
<ul class="menuItems">
<li ng-repeat="item in menuItems">
<div ng-if="item.type == 'link' ">
{{item.name}}
</div>
<div ng-if="item.type == 'function' ">
<span ng-click="function(item.command)" title="item.description">{{item.name}}</span>
</div>
</li>
</ul>

Resources