AngularJS ng-switch rendering order - angularjs

I have a question about ng-switch rendering order.
Code(HTML):
<div ng-controller="testCtrl">
<button ng-click="v1 = !v1">toggle v1</button>
<table class="table table-bordered">
<tbody>
<tr ng-switch on="v1">
<th ng-switch-when="true">V1</th>
<th ng-repeat="item in datas">{{item}}</th>
</tr>
</tbody>
</table>
Code(Javascript):
var myApp = angular.module('myApp',[]);
function testCtrl($scope) {
$scope.datas = ['A', 'B'];
}
I expected a result by the order :
V1 A B
But instead, the output shows ng-repeat items first, followed by ng-switch-when statement.
Why's that?
Is there an alternative solution to do such thing in my expected order?
JSFiddle sample here.
============================
Update (2014/04/02) :
Here's the explanation for why ng-show is not an option.
I write a directive table like this :
<table class="table table-bordered">
<thead>
<tr>
<th ng-show="checkbox"><input type="checkbox" /></th>
<th ng-repeat="col in columns">{{col.text}}</th>
</td>
</thead>
<!-- body is ignored -->
</table>
The checkbox variable is for controlling whether the checkbox input should be showed.
This works fine until I apply JQuery ColResize.
When the checkbox variable is false, the ng-show hides the first th element.
And somehow the hidden th element causes JQuery ColResize to malfunction.
Therefore ng-show is not suitable for this situation.
========================
Current Solution :
My current solution is using ng-switch to separate two table element.
And use one variable to decide which table to draw.
Like this :
<div ng-switch on="checkbox">
<table ng-switch-when="true">
<tr>
<th><input type="checkbox" /></th>
<th ng-repeat="col in columns">{{col.text}}</th>
</tr>
<!-- body is ignored -->
</table>
<table ng-switch-default>
<tr>
<th ng-repeat="col in columns">{{col.text}}</th>
</tr>
<!-- body is ignored -->
</table>
</div>
Although this solve the problem, it is not a good solution.
But until I find an alternative solution.
I think I will have to settle for this.

You can use ng-show instead of ng-switch as shown in this modified JSFIDDLE
<tr>
<th ng-show="v1">V1</th>
<th ng-repeat="item in datas">{{item}}</th>
</tr>
ng-show applies style around the DOM element where the directive is used to show/hide respectively. In your case the header is compiled appropriately but will only be shown based on the condition that is provided to ng-show.

Related

Div inside tbody with ng-if not filtering the result

First of all let me please accept angular and html is not my trade.
Following is my scenario
<tbody ng-repeat="item in items.ItemList">
<div ng-if="item.variableName == false">
<tr>
<tr/>
<tr ng-show="isPermitted" ng-animate="isPermitted">
<tr/>
</div>
</tbody>
I am trying to control the table rows that I wan't to display with ng-if inside the div and it is not working. By moving ng-if to tbody will filter the rows fine but not at div level.
Unfortunately, I can't move ng-if to the tbody as I have to deal with the else part of the if condition and in that case there is an array inside each item of the repeater that should render rows separately.
Following is the output that I want to have once ng-if condition works appropriately.
P.S. I am not specific to the <div> tag here. I am open to replace div with any other tag as long as it can get me the desired result.
Here someone might say why not ng-if at <tr> level? I am using ng-show and ng-animate so the row with ng-if changes its scope in Doom object and hover over functionality stops working.
<tbody ng-repeat="item in items.ItemList">
<div ng-if="item.variableName == false">
<tr>
<tr/>
<tr ng-show="isPermitted" ng-animate="isPermitted">
<tr/>
</div>
<div ng-if="item.variableName == true">
<tr>
<tr/>
<trng-show="isPermitted" ng-animate="isPermitted">
<tr/>
</div>
</tbody>
I use this and it's work.
Look exemple in the jsfilddle
http://jsfiddle.net/ADukg/12003/
<tbody ng-repeat="item in items.ItemList" ng-show="item.variableName">
<tr>{{item | json}}</tr>
</tbody>
Div won't help you out here as it defies the rules of tbody according to html.
Use ng-container like this instead, it is of no meaning to HTML so it won't defy the rules and you will get your condition fulfilled
<tbody ng-repeat="item in items.ItemList">
<ng-container ng-if="item.variableName == false">
<tr>
<tr/>
<tr ng-show="isPermitted" ng-animate="isPermitted">
<tr/>
</ng-container>
<ng-container ng-if="item.variableName == true">
<tr>
<tr/>
<trng-show="isPermitted" ng-animate="isPermitted">
<tr/>
</ng-container>
</tbody>
Also in your case you can use ng-switch instead of ng-if like this
<tbody ng-repeat="item in items.ItemList" ng-switch="item.variableName">
<ng-container ng-switch-when="false">
<tr>
<tr/>
<tr ng-show="isPermitted" ng-animate="isPermitted">
<tr/>
</ng-container>
<ng-container ng-switch-when == "true">
<tr>
<tr/>
<trng-show="isPermitted" ng-animate="isPermitted">
<tr/>
</ng-container>
</tbody>

ng-repeat in combination with ng-init

Controllers loads $scope.shops on init already. Async with defer and promise etc.. I create a panel for each shop there is available. Then I'd like to add columns for each item.
I have a method in controller getItemsPerShop(item) which is also async etc.. I currently get two panels (there are two shops), but the items are the same.. probably cause of the async issue and the $scope.items not getting downloaded fast enough, how would I go about solving this.
<div ng-repeat="shop in shops">
<div class="panel panel-default">
<div class="panel-heading">shop {{shop}}</div>
<table class="table">
<thead ng-init="getItemsPershop(shop)">
<tr>
<th>Number</th>
<th>Date</th>
</tr>
</thead>
<tbody ng-repeat="item in items">
<tr>
<th scope="row">{{item.nr}}</th>
<td>{{item.desc}}</td>
</tr>
</tbody>
</table>
</div>
</div>
It's like a nested situation where the each panel need to load it's rows.
I will go for making getItemsPershop() in controller.
Did you forget the items = in ng-init?
<div ng-repeat="shop in shops">
<div class="panel panel-default">
<div class="panel-heading">shop {{shop}}</div>
<table class="table">
<!-- forgot "items =" ? -->
<thead ng-init="items = getItemsPershop(shop)">
<tr>
<th>Number</th>
<th>Date</th>
</tr>
</thead>
<tbody ng-repeat="item in items">
<tr>
<th scope="row">{{item.nr}}</th>
<td>{{item.desc}}</td>
</tr>
</tbody>
</table>
</div>
</div>
The way your template looks, you have one array items in your scope. However, you need to nest it in the shop, otherwise you can't distinguish them.
In the end it will look like this:
<tbody ng-repeat="item in shop.items">
<tr>
<th scope="row">{{item.nr}}</th>
<td>{{item.desc}}</td>
</tr>
</tbody>
As pointed out in another answer you can assign the items to a field items in a scope local to the current shop. However I would discourage you from doing so. From the docs:
There are only a few appropriate uses of ngInit, such as for aliasing special properties of ngRepeat, as seen in the demo below; and for injecting data via server side scripting. Besides these few cases, you should use controllers rather than ngInit to initialize values on a scope.
In angular you are supposed to init a model in your controller and render it via a template. Mixing these two makes your code harder to understand, test, and maintain.

How to use ng-if with tr tag along with ng-repeat?

Can someone help me on this? Below is my code and the problem explanation.
<table>
<thead>
<tr>
<th>Icon</th>
<th>Name</th>
<th>Details</th>
</tr>
</thead>
<tbody ng-repeat="category in editor.categories">
<tr>
<th>{{category.categoryName}}</th>
</tr>
<tr ng-repeat="feature in category.features"> // Question?
<td>{{feature.iconImg}}</td>
<td>{{feature.featureName}}</td>
<td>{{feature.details}}</td>
</tr>
<tr ng-if="feature.id==1"> // condition 1
</tr>
<tr ng-if="feature.id==2"> // condition 2
</tr>
</tbody>
</table>
Now, let me explain the problem. I have a group of Categories. Each and every category as a group of features.
Now my question is, how can these feature for every category can be displayed based on the ng-if condition. i.e., the <tr> at condition 1 should be displayed only if feature.id==1 and same goes for condition 2 as well only if feature.id==2.
Q. How do I include my ng-if condition based on the different conditions?
Your help is very much appreciated.
Use both ng-if and ng-repeat within one tag:
<tr ng-repeat="feature in features"
ng-if="feature.id === 1" >
</tr>
Or, if you want to conditionally display some templates you may use ng-switch for example:
<tr
ng-repeat="feature in features"
ng-switch="feature.id" >
<span ng-switch-when="1"></span>
<span ng-switch-when="2"></span>
</tr>
check ng-switch and ng-switch-when
https://docs.angularjs.org/api/ng/directive/ngSwitch
Regards

How to conditionally enter column markup based on ng-repeat table?

What I'm trying to do is to conditionally change what is displayed within a td column block, based on some value of my ng-repeat's iterated object value. More specifically, if you look at the code below, what I want to do, is based on some value of data.col2val, I want to change what is displayed in column2 for that row.
I know that there is no such thing as a ng-if-else, but if there was, my code might look something like this:
<table class="table" id="myTable">
<thead>
<tr>
<th>Column1</th>
<th>Column2</th>
</tr>
</thead>
<tbody ng-repeat="data in dataStore">
<tr>
<td>{{data.col1val}}</td>
<td ng-if="{{data.col2val}}='something'" style="somestyle">something</td>
<td ng-if-else="{{data.col2val}}='somethingelse'" style="someotherstyle">somethingelse</td>
</tr>
</tbody>
</table>
Maybe there is a trivial way to do this, but I can't seem to figure/google it out. I know how do to this what ASP.NET/Razor but not with Angular, so any assistance is appreciated and pardon my ignorance.
ng-if="data.col2val=='something'"
you don't have tu use curly braces inside ng-if directive
var app = angular.module('app', []);
app.controller('firstCtrl', function($scope){
$scope.dataStore = [
{col1val:"Mike", col2val:"something"},
{col1val:"Mike", col2val:"somethingelse"},
];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="app">
<div ng-controller="firstCtrl">
<table class="table" id="myTable">
<thead>
<tr>
<th>Column1</th>
<th>Column2</th>
</tr>
</thead>
<tbody ng-repeat="data in dataStore">
<tr>
<td>{{data.col1val}}</td>
<td ng-if="data.col2val=='something'" style="somestyle">something</td>
<td ng-if="data.col2val=='somethingelse'" style="someotherstyle">somethingelse</td>
</tr>
</tbody>
</table>
</div>
</body>

angularjs: ng-repeat-start and ng-repeat-end with inner ng-repeat

Hi I have a simple use case for ng-repeat-start and end and is working just fine, the problem appears when I want to add an inner ng-repeat.
Here is the my code
<tr ng-repeat-start="obj in rows" >
<td ng-repeat="e in obj.row">{{e}}</td>
</tr>
<tr ng-repeat-end>
<td colspan="4">{{obj.description}}</td>
<tr>
The inner ng-repeat into td element is not working, I'm seeing the ngRepeat comment when I inspect the html source code, but the td elements are not being created.
<!-- ngRepeat: e in obj.row -->
My ugly workaround (given that I know the size of that vector) is:
<tr ng-repeat-start="obj in rows" >
<td>{{obj.row[0]}}</td>
<td>{{obj.row[1]}}</td>
<td>{{obj.row[2]}}</td>
<td>{{obj.row[3]}}</td>
</tr>
<tr ng-repeat-end>
<td colspan="4">{{obj.description}}</td>
<tr>
I am not sure whether you are using angular 1.1.6 or not since ng-repeat-start and ng-repeat-end are not available in 1.1.5 or 1.0.7 yet.
However, you don't actually have to use the new directives to achieve that. You can simply implement it like this for right now:
<table>
<tbody ng-repeat="obj in rows">
<tr ng-repeat="e in obj.row">
<td>{{e}}</td>
</tr>
<tr>
<td colspan="4">{{obj.description}}</td>
<tr>
</tbody>
</table>
You may use ng-repeat-start and ng-repeat-end to reimplement it when AngularJS 1.1.6 version is officially released.
Demo
I think it might be something wrong with your data structure. Using Angular 1.2.1 this works for me
<div ng-app="myApp" ng-controller="myCtrl">
<div class="h" ng-repeat-start="val in data">{{val.title}}</div>
<div ng-repeat="con in val.content">
{{con}}
</div>
<div class="b" ng-repeat-end>footer</div>
</div>
See jsFiddle
You should be able to use index-based iterations to bypass that:
<tr ng-repeat-start="obj in rows" >
<td ng-repeat="e in obj.row">{{obj.row[$index]}}</td>
</tr>
<tr ng-repeat-end>
<!-- ... -->

Resources