ng-repeat and accessing item elements - angularjs

I'm using ng-repeat to generate a table (for example):
<table ng-repeat="item in items">
<tr>
<td>
<input ng-blur="changeMyItem($event)">{{item.Name}}</input>
<td>
<td>
<div>{{itemError}}</div>
</td>
</tr>
</table>
I am making a call that could potentially fail and I am having a hard time figuring out how to set itemError above to the error message after the page has been rendered when there is an error.
From my changeMyItem method (triggered by ng-blur) I've tried the expression syntax and it just becomes an empty div. I've also tried calling $scope.$apply in my success callback and it says $digest is already in progress and errors.
Without using a custom directive, how can I update itemError above with the related error?

Mike,
If you are trying to set an error message for each individual input, this should do.
<table ng-repeat="item in items">
<tr>
<td>
<input ng-blur="changeMyItem(item, $event)" ng-model="item.Name">
<td>
<td>
<div>{{item.ErrorMessage}}</div>
</td>
</tr>
</table>
In you controller, you can have something similar to:
$scope.changeMyItem = function(item, $event) {
if(!item.Name) {
item.ErrorMessage = "This has error";
}
}

Well, I think the easiest way would be to ng-init a new object for each repeated table, and pass that to the changeMyItem function:
<table ng-repeat="item in items" ng-init="itemError = { message : '' }" >
<tr>
<td>
<input ng-blur="changeMyItem($event, itemError)">{{item.Name}}</input>
</td>
<td>
<div>{{itemError.message}}</div>
</td>
</tr>
</table>
Use an object with a property so that you pass it by reference to the function.
Then you can do:
$scope.changeMyItem = function(event, error) {
//whatever
error.message = "It didn't work!";
}

You really should use a service:
<table ng-repeat="item in myForm.items track by $index">
<tr>
<td>
<label>{{item.Title}}</label>
<input ng-blur="myForm.changeMyItem($event, $index)" ng-model="item.Value">
<td>
<td>
<div>{{item.Error}}</div>
</td>
</tr>
</table>
Then
app.factory('myForm', function(){
var myform = {};
myForm.items = [{ ... }];
myForm.changeMyItem = function(event, index){
};
myForm.submit = function(){
};
myForm.setErrors = function(){
};
...
return myForm;
});

Related

ngTable with icon outside of table

I would want the trash bin icon to appear outside of the table. Right now its inside the table and upon hovering, the trashbin icon would appear before pushing the content to the right. How do i make it such that the trash bin icon would appear outside of the table beside each content?
JS
$scope.tdemo = {};
// table dataset
$scope.myDataset = [
{
name: 'eeee',
lastname: 'dada',
},
{
name: 'abc',
lastname: 'kfc',
}
];
$scope.hoverIn = function () {
this.hoverEdit = true;
};
$scope.hoverOut = function () {
this.hoverEdit = false;
};
HTML
<table ng-table="twinTableParams" class="table hover">
<tbody>
<tr ng-repeat="trans in $data" ng-mouseover="hoverIn()"
ng-mouseleave="hoverOut()">
<td>
<span ng-show="hoverEdit" class="animate-show">
<a><i class="fi-trash"></i></a>
</span>
</td>
<td ng-model="name" title="'name'">{{trans.name}}</td>
<td ng-model="lastname" title="'lastname'">{{trans.lastname}}</td>
</tr>
</tbody>
</table>
In order to do that (I don't really understand what's the good UX for it). You can set this as a float element and move it outside the table.
Change this to $scope. You can use this if you are using controller As syntax
$scope.hoverEdit=[];
$scope.hoverIn = function (index) {
$scope.hoverEdit[index] = true;
};
$scope.hoverOut = function (index) {
$scope.hoverEdit[index] = false;
};
And change your HTML to the following
<table ng-table="twinTableParams" class="table hover">
<tbody>
<tr ng-repeat="trans in $data" ng-mouseover="hoverIn($index)" ng-mouseleave="hoverOut($index)">
<td ng-model="name" title="'name'">
{{trans.name}}
</td>
<td ng-model="lastname" title="'lastname'">
{{trans.lastname}}
</td>
</tr>
</tbody>
</table>
<div ng-repeat="trans in $data">
<span ng-show="hoverEdit[$index]" class="animate-show" >
<a>
<i class="fi-trash"></i>
</a>
</span>
</div>

ng-repeat with click modifying input

<input ng-model = "val">
<a href ng-click="val = 1"> val = 1 </a>
<div class="test" ng-controller="Ctrl">
<table>
<thead>
<tr>
<th>let</th>
<th>num</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="thing in data">
<td>
<a href ng-click="val = 1">
{{thing.let}}
</a>
</td>
<td>{{thing.num}}</td>
</tr>
</tbody>
Is there a way to make an input change based on a click in an ng-repeat?
In this jsfiddle you can the input change with a click outside of an ng-repeat, but in the repeat it doesn't change the input.
http://jsfiddle.net/Hp4W7/2403/
The problem is that you are setting the val property in a child scope created by ng-repeat.
The solution is to create a function that assigns this value to the parent scope:
$scope.changeVal = function(val){
$scope.$parent.val = val;
}
And call it with ng-click="changeVal(1)"
Fiddle here: http://jsfiddle.net/nawd7jjc/
ng-repeat always make new scope for every iteration so if you change any primitive value inside ng-repeat ("val" in this case) then it will not refer to the actual "val". So to solve it, it should be a object type, for ex. obj.val something
Below is the working solution for this problem:
<div class="test" ng-controller="Ctrl" ng-init="obj.val=12345">
<input ng-model = "obj.val">
<a href ng-click="obj.val = 2"> val = 2 </a>
<table>
<thead>
<tr>
<th>let</th>
<th>num</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="thing in data">
<td>
<a href ng-click="obj.val = thing.num">
{{thing.let}}
</a>
</td>
<td>{{thing.num}}</td>
</tr>
</tbody>
</table>
<div>

How to display "No data" message using AngularJS?

The code looks as following:
<table>
<tr ng-repeat="action in actionList">
<td></td>
...
</tr>
<tr ng-show="!actionList.length">
<td>
No data.
</td>
</tr>
</table>
This works, but not in way I want because it initially displays "No data" message. I need to display nothing initially and the table with data if actionList.length is not 0. If actionList.length is 0 then display "No data" message, but not as a part of table.
Update:
I've added in my controler:
$scope.isButtonClicked = false;
$scope.queryResult = function () {
$scope.isButtonClicked = true;
$scope.actionList = [];
$scope.actionsQuery.resetPage();
appendPage();
};
But, "No data." message appears on every click. When there's some data it appears very shortly and disappears and when there's no data, it appears correctly.
Why you confusing? Lot of way of you can think to solve this.
Try this simple way
<table>
<tr ng-show="actionList.length!=0" ng-repeat="action in actionList">
<td></td>
...
</tr>
<tr >
<td ng-show="actionList.length==0">
No data.
</td>
</tr>
</table>
Update 1:-
Also you need to check actionList is undefined or null
And You should $scope.actionList is a array
You can check the actionList is null or undefined with ng-if Statement (try with ng-if instead of ng-show in the div tag)
Something like this below way
<div ng-show="actionList !='null' && actionList !='undefined'">
<table>
<tr ng-show="actionList.length!=0" ng-repeat="action in actionList">
<td></td>
...
</tr>
<tr >
<td ng-show="actionList.length==0">
No data.
</td>
</tr>
</table>
</div>
<div ng-show="actionList =='null'|| actionList =='undefined'">
No data.
</div>
Update 2 :-
Another simple way to instead of my update 1
try this below code.
<table>
<tr ng-show="actionList!='undefined' && actionList!='null' && actionList.length!=0" ng-repeat="action in actionList">
<td></td>
...
</tr>
<tr >
<td ng-show="actionList=='undefined' ||actionList=='null' || actionList.length==0 ">
No data.
</td>
</tr>
</table>
Here you can try with ng-if instead of ng-show
Update 3:
I want because it initially displays "No data" message. I need to display nothing initially and the table with data if actionList.length is not 0. If actionList.length is 0 then display "No data" message, but not as a part of table.
So Simple ...
Create a new Boolean Scope variable like a named as IsButtonClick
Globally declare $scope.IsButtonClick==false
Set $scope.IsButtonClick==true on your button click event
Then copy past this below code instead of your html code.
<div ng-show="IsButtonClick">
<table>
<tr ng-show="actionList!='undefined' && actionList!='null' && actionList.length!=0" ng-repeat="action in actionList">
<td></td>
...
</tr>
</table>
</div>
<div ng-show="IsButtonClick"> <span ng-show="actionList=='undefined' ||actionList=='null' || actionList.length==0 "> No data.</span> </div>
Update 4 :
But, "No data." message appears on every click. When there's some data it appears very shortly and disappears and when there's no data, it appears correctly
try this way
$scope.queryResult = function () {
$scope.isButtonClicked = false;
$scope.actionList = [];
$scope.actionsQuery.resetPage();
appendPage();
if($scope.actionList.Length==0)
{
$scope.isButtonClicked = true;
}
};
and the html is
<div ng-show="IsButtonClick">
<table>
<tr ng-show="actionList!='undefined' && actionList!='null' && actionList.length!=0" ng-repeat="action in actionList">
<td></td>
...
</tr>
</table>
</div>
<div ng-show="IsButtonClick"> <span ng-hide="actionList !='undefined' || actionList!='null' || actionList.length!=0 "> No data.</span> </div>
Try this :
<div ng-switch="!!actionsList.length">
<div ng-switch-when="true">
<table>
<tr ng-repeat="...">
<td> ... </td>
</tr>
</table>
</div>
<div ng-switch-when="false">
No data
</div>
</div>
try to put
<tr ng-show="!actionList.length">
<td ng-bind="noData">
</td>
</tr>

Loading text before data is loaded AngularJS

Does anyone have a clue how to create a loading text before angular loads data in to a list?
I have this code for getting info from different subwebs (in sharepoint). But before (or while) the list is loaded I want to display a text "Loading...":
context.executeQueryAsync(Function.createDelegate(this, function () {
var webs = this._webs.getEnumerator();
while (webs.moveNext()) {
var subWeb = webs.get_current();
$scope.sites.push({
title: webs.get_title(),
});
$scope.$apply();
}
}
}),
HTML:
<div ng-controller="MainCtrl">
<table>
<thead>
<tr>
<th>
Site
</th>
<th>
Created
</th>
</tr>
</thead>
<tbody ng-repeat="site in sites">
<tr>
<td>
{{site.title}}
</td>
<td>
{{site.created | date:dateFormat}}
</td>
</tr>
</tbody>
</table>
<tbody ng-if="sites.length == 0">
<tr>
<td>
<img src="loading.gif">
</td>
</tr>
</tbody>
<tbody ng-repeat="site in sites" ng-if="sites.length > 0">
<tr>
<td>
{{site.title}}
</td>
<td>
{{site.created | date:dateFormat}}
</td>
</tr>
</tbody>
This produce 2 watchers but it will help.
some links of loading indicator : angular-spinner or angular-sham-spinner
also BLOG detailing on how the spinner works with angularjs
and in case you want to implement it yourself, below code will get you started...
app.directive("spinner", function(){
return: {
restrict: 'E',
scope: {enable:"="},
template: <div class="spinner" ng-show="enable"><img src="content/spinner.gif"></div>
}
});
warning : i havent tested the code but directive wont be more complex than this...

ng-show does not work with custom directive

I've just started using AngularJS and wanted to create a custom template directive for creating "in-place" editable tables. The idea would be to have something like:
<tr ng-repeat="customer in model.customers">
<ng-template ng-hide="customer === model.selectedCustomer"> <!-- display template-->
<td>{{customer.name}}</td>
</ng-template>
<ng-template ng-show="customer === model.selectedCustomer"> <!-- edit template -->
<td><input type="text" ng-model="customer.name"/></td>
</ng-template>
</tr>
It could then also be extended to specify a templateUrl e.g. <ng-template template-url="foo.html"></ng-template>
When I apply the ng-show directive to my custom directive it does not work. Here's the code for my directive:
var demo = angular.module("demo", [])
.directive("ng-template", function() {
return {
restrict: "E",
replace: true,
transclude: true
}
});
and HTML (http://jsfiddle.net/benfosterdev/ASXyy/):
<div ng-app="demo">
<table>
<tr ng-repeat="name in ['jane', 'john', 'frank']">
<ng-template ng-show="name !== 'frank'">
<td>{{name}}</td>
</ng-template>
</tr>
</table>
</div>
Furthermore, when I look at the generated HTML my custom directive doesn't even appear in the table:
<div ng-app="demo" class="ng-scope">
<ng-template ng-show="name !== 'frank'" class="">
</ng-template>
<table>
<tbody>
...
</tbody>
</table>
</div>
Essentially I'm trying to avoid writing code like this (setting the ng-show directive on every <td> element):
<table>
<tr ng-repeat="customer in customers">
<ng-template>
<td ng-hide="isSelected">{{customer.name}}</td>
<td ng-hide="isSelected">{{customer.age}}</td>
<td ng-hide="isSelected"><button ng-click="edit(customer)"</td>
<td ng-show="isSelected"><input type="text" ng-model="customer.name"/></td>
<td ng-show="isSelected"><input type="text" ng-model="customer.age"/></td>
<td ng-show="isSelected"><button ng-click="save(customer)"</td>
</ng-template>
</tr>
</table>
A couple of things occur to me when I look at your code.
ng-include offers very similar functionality to your proposal for extending ng-template. If you're going to load a view based on the state of the underlying model then I think this would be the way to go.
If you're not going to be loading the template from a separate view file, why not just use ng-show (or ng-if / ng-switch, which I prefer in most cases) on your td element?
Here is some example code using ng-include:
<table>
<thead>
<th>One</th>
<th>Two</th>
<th>Three</th>
<th></th>
</thead>
<tbody>
<tr ng-repeat="item in items" ng-include="getTemplate(item)"></tr>
</tbody>
</table>
Here is the full JSFiddle: http://jsfiddle.net/qQR6j/2.
Use ng-repeat-start and ng-repeat-end to specify the two alternative <tr> tags.
<div ng-app="demo">
<table ng-controller="Ctrl">
<tr ng-repeat-start="name in ['jane', 'john', 'frank']" ng-hide="isSelected(name)">
<td>{{name}} <button ng-click="select(name)">edit</button></td>
</tr>
<tr ng-repeat-end ng-show="isSelected(name)">
<td>{{name}}!</td>
</tr>
</table>
</div>
With this javascript
var demo = angular.module("demo", []);
demo.controller("Ctrl",
function Ctrl($scope) {
var selected;
$scope.isSelected = function(name) {
return selected === name;
};
$scope.select = function(name) {
selected = name;
};
});
Live example: http://jsfiddle.net/6FtjG/1/
Your browser renders the 'ng-template' outside of the table because its not a valid child of tr. Even if you have set replace to true, the directive needs to be rendered before it can be replaced.
You can see it is because of the table, because this does work:
<div>
<div ng-repeat="name in ['jane', 'john', 'frank']">
<ng-template ng-show="name !== 'frank'">
<div >{{name}}</div>
</ng-template>
</div>
</div>
see: Fiddle
This is something your browser does so you cannot avoid it.

Resources