I am building my first angular-meteor app.
Find below the app.js file:
libri = new Mongo.Collection("libri");
if (Meteor.isClient) {
angular.module('bookshelf',['angular-meteor']);
angular.module('bookshelf').controller('BookListCtrl', ['$scope','$meteor',
function($scope,$meteor){
$scope.libri = $meteor.collection(libri)
$scope.counter = 0;
for(i = 0; i < libri.find().count(); i++){
$scope.counter += 1;
}
}]);
}
if (Meteor.isServer) {
Meteor.startup(function () {
});
}
and the index.ng.html file:
<div class="container">
<table class="table" ng-controller="BookListCtrl">
<caption>Optional table caption.</caption>
<thead>
<tr>
<th>Contatore</th>
<th>Titolo</th>
<th>Autore</th>
<th>Casa Editrice</th>
</tr>
</thead>
<tbody ng-repeat="libro in libri">
<tr>
<td>{{counter}}</td>
<td>{{libro.titolo}}</td>
<td>{{libro.autore}}</td>
<td>{{libro.casaEditrice}}</td>
</tr>
</tbody>
</table>
</div>
And I get the following table:
As you can see the auto increment field {counter} is always 3. I would like to be 1,2,3 for each line of the table without creating another document-field in the collection ('libri'). That cell of the table is not part of the mongo collection.
Is there any suggestion? Why I cannot print the loop in the html table? Thanks
The reason why you are always getting only number 3 is because that is what is being prepared for the view.
When the view gets the data front the controller, the counter is already at the value 3.
What you should do is use $index instead of {{counter}} and it will automatically get the index from the ng-repeat iterating over the array, so you will get 1, 2, 3....
If you wanted to send it with your data from the controller, then you should imbue the index with your array.
Something like this:
var tempArr = [
{name: 'a'},
{name: 'b'},
{name: 'c'}
]
var i = 0;
for (; i < tempArr.length; i++) {
tempArr[i].index = i;
}
console.log('updated array', tempArr);
Just replace tempArr with your data, and it should run smoothly.
You can test this here, just check your console.
Hope this helps :)
Use {{$index}} instead of {{counter}}.
Related
I'm new to GAS and I'm trying to create an app that displays a searchable list of people, click on a unique id number and open another page to update data. I got the search to work using a different method but would only return data to the logs and not to the html table properly. I stumbled across this post last night which I thought would enabled me to create the link but I don't know how to display each column independently with the multi-dimensional array. The code below spits out the entire row, but I need the first column to be linked to a url that changes for each row depending on the data in the first column. I figured I would try to create a new column with the link calculated and if I grabbed that column instead it would work but no dice (this is why I'm pulling J instead of A). so row1 first column would be mylink.com?id=36, row2 would be mylink.com?id=400, etc.
So in summary I have 3 issues, if 2 & 3 are combined I'm ok with that.
filter/search multiple columns (with wildcards?) return results.
display multi-dimensional array each column independently (mainly to do #3).
grab the id in the first column and create a clickable url based upon the id in the first column.
code.gs
function getData()
{
var targetRange = 'SearchList!A:J';
var SQL = 'select J, B, C, D where J is not null';
var Query = '=QUERY('+targetRange+',\"'+SQL+'\")';
Logger.log(Query);
var currentDoc = SpreadsheetApp.openByUrl(url);
var tempSheet = currentDoc.insertSheet();
var pushQuery = tempSheet.getRange(1, 1).setFormula(Query);
var pullResult = tempSheet.getDataRange().getValues();
Logger.log(pullResult);
currentDoc.deleteSheet(tempSheet);
return pullResult;
}
html
<table class="table table-striped table-bordered table-hover">
<thead>
<td>Scripta ID</td>
<td>Last Name</td>
<td>First Name</td>
<td>Birth Date</td>
</thead>
<tbody id="table-body">
<? var data = getData();
for (var i = 0; i < data.length; i++)
{
?>
<tr>
<? for (var j = 0; j < data[i].length; j++)
{
Logger.log(data[i][j]);
?>
<td><a href="<?= ScriptApp.getService().getUrl(); ?>?f=member&sid="&<?= data[i][j] ?>><?= data[i][j] ?></a></td>
<? }
?>
</tr>
<? }
?>
</tbody>
</table>
To get column A only:
function getOne() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
var vs=sh.getRange(1,1,sh.getLastRow(),1).getValues();
vs.forEach(function(r,i){
//r[0] is columnA for each row
});
//or
for(var i=0;i<vs.length;i++) {
//vs[i][0] is column A for each row
}
}
Sheet.getRange(row, col, numrows, numcols)
I have a problem to sort my data with smart table, specifically when including a turkish character. Wrong order is generated.
In my controller:
$scope.rowCollection = [{
a: 'Çanakkale',
b: '3'
}, {
a: 'Ceyhan',
b: '2'
}, {
a: 'ĞĞĞĞĞ',
b: '4'
}, {
a: 'Ankara',
b: '1'
}, {
a: 'Zonguldak',
b: '5'
}];
$scope.displayedCollection = [].concat($scope.rowCollection);
and my html:
<tr ng-repeat="row in displayedCollection">
<td ng-repeat="col in columns">{{row[col]}}</td>
</tr>
Here's the plunk:
http://plnkr.co/edit/JW4G1n2QszIqYjcAmlNz
How can i fix it ?
Thanks for your help
This is what I've found for you:
The smart-table version in your plunk is missing some parts (line 164) which doesn't allow you to do what you want. I've changed it to version 2.1.8 in my plunk
Use st-set-sort="yourFilterName" on your table, where your st-table attribute is:
<table st-table="displayedCollection" st-set-sort="turkishFilter" st-safe-src="rowCollection" class="table table-striped">
Write a custom filter function:
angular.module('myApp', ['smart-table'])
.filter('turkishFilter', function(){
return function(items, field, isDescending){
//If you don't create a copy of the array,
//smart-table won't be able to restore the natural order state
var result = items.slice();
//Working only for string properties ATM!
result.sort(function(first, second){
//return first.a.localeCompare(second.a, 'tr');
//OR
return first[field].localeCompare(second[field], 'tr');
//localCompare() is supported only in IE11 and upwards
});
if (isDescending){
result.reverse();
}
return result;
};
})
Working plunk HERE
I used ng-resource to get data from my server and then place the data into a table grid like this:
<div ng-form name="grid">
<button type="submit" data-ng-disabled="grid.$pristine">Save</button>
<div class="no-margin">
<table width="100%" cellspacing="0" class="form table">
<thead class="table-header">
<tr>
<th>ID</th>
<th>Title</th>
</tr>
</thead>
<tbody class="grid">
<tr data-ng-repeat="row in grid.data">
<td>{{ row.contentId }}</td>
<td><input type="text" ng-model="row.title" /></td>
</tr>
</tbody>
</table>
</div>
</div>
Is there a way that I can make it so that clicking on the Submit button checks through the grid for the rows that changed and then calls a putEntity(row) function with the row as an argument?
You could do it a few ways, and remember every NgModelController has a $dirty flag which can use to check if the input has changed. But I would say the easiest way is just to do this:
Edit to HTML:
<input type="text" ng-model="row.title" ng-change="row.changed=true" />
<button ng-click="save()">Save</button>
In JS:
$scope.save = function () {
// iterate through the collection and call putEntity for changed rows
var data = $scope.grid.data;
for (var i = 0, len = data.length; i < len; i++) {
if (data[i].changed) {
putEntity(data[i]);
}
}
}
Here's something that could work. It is built with the JSFiddle from the first comment as a basis.
First, I changed the data-ng-disabled attribute to changes.length <= 0 and added $scope.changes = [] to the controller.
$scope.changes = [];
Then I added a watch on $scope.data
$scope.$watch('data', function(newVal, oldVal){
for(var i = 0; i < oldVal.length; i++){
if(!angular.equals(oldVal[i], newVal[i])){
console.log('changed: ' + oldVal[i].name + ' to ' + newVal[i].name);
var indexOfOld = $scope.indexOfExisting($scope.changes, 'contentId', newVal[i].contentId);
if(indexOfOld >= 0){
$scope.changes.splice(indexOfOld, 1);
}
$scope.changes.push(newVal[i]);
}
}
}, true); // true makes sure it's a deep watch on $scope.data
Basically this runs through the array and checks if anything has changed using angular.equals. If an object has changed it is checked if it exists in $scope.changes already. If it does, it's removed. After that newVal[i] is pushed to $scope.changes
The $scope.indexOfExisting is taken from this SO question
$scope.indexOfExisting = function (array, attr, value) {
for(var i = 0; i < array.length; i += 1) {
if(array[i][attr] === value) {
return i;
}
}
};
Finally I made the $scope.checkChange() look like so
$scope.checkChange = function(){
for(var i = 0; i < $scope.changes.length; i++){
console.log($scope.changes[i].name);
//putEntity($scope.changes[i])
}
$scope.changes = [];
};
This will then give you the ability to submit only the changed rows.
I decided to do this as follows:
Here is where I get the data and then make a copy of it:
getContents: function ($scope, entityType, action, subjectId, contentTypeId, contentStatusId) {
entityService.getContents(entityType, action, subjectId, contentTypeId, contentStatusId)
.then(function (result) {
$scope.grid.data = result;
$scope.grid.backup = angular.copy(result);
$scope.grid.newButtonEnabled = true;
}, function (result) {
alert("Error: No data returned");
$scope.grid.newButtonEnabled = false;
});
},
Then later when it comes to saving I use angular.equals to compare with a backup:
$scope.saveData = function () {
var data = $scope.grid.data;
for (var i = 0, len = $scope.grid.data.length; i < len; i++) {
if (!angular.equals($scope.grid.data[i], $scope.grid.backup[i])) {
var rowData = $scope.grid.data[i]
var idColumn = $scope.entityType.toLowerCase() + 'Id';
var entityId = rowData[idColumn];
entityService.putEntity($scope.entityType, entityId, $scope.grid.data[i])
.then(function (result) {
angular.copy(result, $scope.grid.data[i]);
angular.copy(result, $scope.grid.backup[i]);
}, function (result) {
alert("Error: " + result);
})
}
}
$scope.grid.newButtonEnabled = true;
$scope.grid.saveButtonEnabled = false;
$scope.$broadcast('tableDataSetPristine');
}
I did something quite similar for myself, and I used a different way, I decided to directly bind all the ng-models to the data, which let me work with the indexes if I need to check every row, but I could also decide to send the whole data to the server
Then, I just have to watch all the changes made to every index (even with big data, I just have to add/remove an integer) and store them in an array
Once finished, I can click the submit button, which will loop on every index I've touched, to check if the content is really different from the original one, and then I can do whatever I want with it (like calling a putEntity function, if I had one)
I made a fiddle based on your html code, it will be easier to play with it than with a copy/paste of the code here : http://jsfiddle.net/DotDotDot/nNwqr/1/
The main change in the HTML is this part, binding the model to the data and adding a change listener
<td><input type="text" ng-model="data[$index].title" ng-change="notifyChange($index)"/></td>
I think the javascript part is quite understandable, I log indexes while the data is modified, then, later, when I click on the submit button, it can call the right function on the modified rows only, based on the logged indexes
I'm newbie to AngularJs. I want to use ng-repeat as for(i=0; i < ele.length; i+=2)
I have a table with 4 columns, where I'm going to use ng-repeat
<table>
<tr ng-repeat="i in elements">
<th>{{i.T}}</th>
<td>{{i.V}}</td>
<th>{{elements[($index+1)].T}}</th> <!-- This should be next element of the elements array -->
<td>{{elements[($index+1)].V}}</td> <!-- This should be next element of the elements array -->
</tr>
</table>
I need to access 2 elements in a single iteration and iteration increment should be 2
I hope this make sense. Please help me.
Please check this html view Plunker
You can create a filter that creates an even copy of the array:
.filter("myFilter", function(){
return function(input, test){
var newArray = [];
for(var x = 0; x < input.length; x+=2){
newArray.push(input[x]);
}
return newArray;
}
});
JsFiddle: http://jsfiddle.net/gwfPh/15/
So if I understand correctly you want to walk your list and alternate th and td's while iterating.
If so you could use a ng-switch:
<table>
<tr ng-repeat="i in elements" ng-switch on="$index % 2">
<th ng-switch-when="0">{{i.T}}</th>
<td ng-switch-when="1">{{i.V}}</td>
</tr>
</table>
See Plunker here
One solution I can think of involves a change in the data model
Template
<table ng-app="test-app" ng-controller="TestCtrl">
<tr ng-repeat="i in items">
<th>{{i.T1}}</th>
<td>{{i.V1}}</td>
<th>{{i.T2}}</th>
<td>{{i.V2}}</td>
</tr>
</table>
Controller
testApp.controller('TestCtrl', ['$scope', function($scope) {
var elements=[]; //This is the dynamic values loaded from server
for (var i = 0; i < 5; i++) {
elements.push({
T : i,
V : 'v' + i
});
}
//A converter which convert the data elements to a format we can use
var items = [];
var x = Math.ceil(elements.length / 2);
for (var i = 0; i < x; i++) {
var index = i * 2;
var obj = {
T1 : elements[index].T,
V1 : elements[index].V
}
if (elements[index + 1]) {
obj.T2 = elements[index + 1].T;
obj.V2 = elements[index + 1].V
}
items.push(obj)
}
$scope.items = items;
}]);
Demo: Fiddle
Another slightly different approach can be found here.
I'm trying to create a dynamic table that could hold search results with different amount of columns.
I created a table that should have a row for every entry and a column for every datafield both populated with ng-repeat -functions, but for some reason it doesn't show any information in the columns at all, although it does create correct amount of them.
If I try to show e in {{}} it shows the correct key that exists. If I try with i in {{}} it shows the following in each column (the information is same for all columns, but different for every row):
{"etunimi":"firstname","sukunimi":"lastname","optunnus":"010101010101011001"}
Here is the html:
<table id="raporttiTulos" class="resultTable">
<tr ng-repeat="i in raportointiLista">
<td ng-repeat=" e in raportointiAvaimet">{{i.e}}</td>
</tr>
</table>
Here is the function responsible for the incoming data:
$scope.haeMaksut = function(){
$scope.raportointiAvaimet = {};
$http.post('/maksuhaku')
.then(function(res){
x = 0;
$scope.raportointiLista = res.data.message;
for(i in $scope.raportointiLista[0]){
$scope.raportointiAvaimet[x] = i;
x+=1
}
console.log($scope.raportointiAvaimet);
$scope.maksamattomat = $scope.raportointiLista.length;
$scope.lataus = true;
}, function(error){
console.log(error);
});
}
This is how the key list looks like:
Object [ "etunimi", "sukunimi", "optunnus" ]
Here are some rows from the data list:
[…]
[0…99]
0: Object { etunimi: "firstname", sukunimi: "lastname", optunnus: "101010101010101010", … }
instead of doing that you can directly access object keys.
HTML
<tr ng-repeat="i in raportointiLista">
<td ng-repeat="key in raportointiAvaimet">{{i[key]}}</td>
</tr>
Controller
$scope.haeMaksut = function () {
$scope.raportointiAvaimet = {};
$http.post('/maksuhaku')
.then(function (res) {
$scope.raportointiLista = res.data.message;
$scope.raportointiAvaimet = Object.keys($scope.raportointiLista[0]);
}, function (error) {
console.log(error);
});
}