ng-repeat in combination with ng-init - angularjs

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.

Related

How to bind object to a table in AngularJS?

I'm trying to databind an object received via an HTTP request to a table in angularjs.
The normal way to do this would be to use ng-repeat as follows.
<table class="table table-striped">
<tr>
<th>name</th>
<th>artist</th>
</tr>
<tr>
<td ng-repeat="track in $scope.trackList.items">
{{ track.name }}
{{ track.artist }}
</td>
</tr>
</table>
The problem with this is that the page loads and ng-repeat is ran before the data is returned from the server causing no items to be in the collection so nothing is drawn into the table.
What would be the best way to do this?
You don't need $scope in your view. Also, you probably want to put ng-repeat on <tr>, not <td>.
<table class="table table-striped">
<tr>
<th>name</th>
<th>artist</th>
</tr>
<tr ng-repeat="track in trackList.items">
<td>{{ track.name }}</td>
<td>{{ track.artist }}</td>
</tr>
</table>
You don't need $scope here just simple trackList.items.
$scope is the glue b/w Views and controller and you don't need to explicitly call $scope in the view it is already implicit that things are already picking from the scope.
<td ng-repeat="track in trackList.items">
{{ track.name }}
{{ track.artist }}
</td>
And if you are talking about the http call then here come's the magic of two way binding into picture.If anything update in controller by $http call that will be reflect in view as well you don't need to do it manually.
Hope it helps. :)
Let me correct your code first
<table class="table table-striped">
<tr>
<th>name</th>
<th>artist</th>
</tr>
<tr>
<td ng-repeat="track in trackList.items">{{ track.name }}</td>
<td>{{ track.artist }}</td>
</tr>
</table>
You don't have to explicitly specify $scope while binding data.
With respect to your problem, even though the code will get executed when the page is getting loaded, any changes that you do to the scoped data will be honored by the angular and view will get updated.
In your case, at the time of page load, if there is data available in trackList.items it will be shown in the page. Otherwise table will be rendered with just headers. Later when the application receives data from AJAX (or any other source), you have to simply assign it to $scope.trackList in your JS code. This will result in instant view updates and you will see the table starts reflecting the new data.

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>

Adding a list of events in angular

I'm not sure how to do this in angular, after coming from jquery.
I have a table:
<div class="col-xs-10">
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
</thead>
<tbody ng-repeat="val in data">
<tr>
<td>val.Time</td>
<td>val.Distance</td>
<td ng-click="callmethod()"><img src="delete"></td>
</tr>
</tbody>
</table>
</div>
Essentially I want the callmethod() to know which row is being clicked so that I can make a update in the model in my controller. What is the right way to do this?
You can use the $index property:
callmethod($index)
Then on your controller you would do something like:
function callmethod(index) {
var foo = $scope.data[index];
}
Change that ng-click to this:
<td ng-click="callmethod(val)"><img src="delete"></td>
You'll get the whole val object when that method gets called.

AngularJS ng-switch rendering order

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.

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