Angular Editable table not working with ng-repeat - javascript

<table class="table table-bordered">
<tbody>
<tr ng-repeat="playerOrTeam in template.editableTable track by $index">
<td style="text-align: center;" ng-repeat="playerOrTeamCat in playerOrTeam track by $index">
<input ng-model="playerOrTeamCat" type="text" class="form-control input-sm">
</td>
</tr>
</tbody>
</table>
template.editableTable is a multi dimensional array just filled with standard variables. when I change one of the values in the input box and then i look at the output of the template.editable table, i don't see the changes. Am I missing something obvious?
EDIT with more details because i'm getting no responses =\
//Template Class
app.factory('Template', function () {
var Template = function () {
/*
* Public Variables
*/
this.editableTable = someMultiDimensionalTable;
}
/*
* Functions
*/
Template.prototype.SeeEditableTableContents = function () {
console.log(this.editableTable);
}
}
//Main Controller
app.controller('MainController', function () {
$scope.template = new Template();
//etc
}

You cannot perform direct in-line modifications within ng-repeat. You can update your array entry using a function.
You'd want something like:
$scope.saveEntry = function (idx) {
console.log("Saving entry");
$scope.template.editableTable[idx] = angular.copy($scope.model.selected);
$scope.reset();
};
See JSFiddle for sample.

okay i actually got it to work so that i CAN make direct in-line modifications with ng-repeat by making my multidimensional table full of objects rather than soley a value.
so by doing that, i modified the table to look like this
<table class="table table-bordered">
<tbody>
<tr ng-repeat="playerOrTeam in template.editableTable track by $index">
<td style="text-align: center;" ng-repeat="playerOrTeamCat in playerOrTeam track by $index">
<input ng-model="playerOrTeamCat.value" type="text" class="form-control input-sm">
</td>
</tr>
</tbody>
</table>
got the idea looking here
Modifying objects within Angular Scope inside ng-repeat

Related

Why is my textbox not displaying data from AngularJS controller

In my application, I can verify that the correct info is being selected by using a console.log, but trying to display this info in a form textbox isn't working. Can someone please tell me what I'm doing wrong?
Here is me view's HTML.
<table class="table table-stripped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="organization in organizations | filter:search" data-ng-click="navToEdit($index)">
<td>{{organization.Id}}</td>
<td>{{organization.Title}}</td>
</tr>
</tbody>
</table>
Here is what clicking an item in that list takes you to.
<div class="form-group">
<label for="txtTitle">Title:</label>
<input type="text" type="text" id="txtTitle" class="form-control" data-ng-model="organization.Title" />
</div>
Here is my controller.
app.controller("organizationsCtrl", ["$scope", "$location", "$routeParams", "spService",
function ($scope, $location, $routeParams, spService) {
var listTitle = "Organizations";
$scope.editing = false;
//example populating Organizations
spService.getRecords(listTitle, "?$select=ID,Title").then(function (result) {
$scope.organizations = result;
});
$scope.navToAdd = function() {
$location.path("/organizations/add");
}
$scope.navToEdit = function(index) {
$scope.organization = $scope.organizations[index];
$location.path("/organizations/" + index);
console.log($scope.organization.Title);
}
}
]);
$scope.navToEdit does output the correct organization, but the textbox of txtTitle doesn't show anything.
Please help!!
As i see , the problem here is you are redirecting to another page here,
$location.path("/organizations/" + index);
which will clear the existing scope within the controller, if you want to transfer the data from one page to another use service or localStorage.

Create a dynamic table in Angular based on the amount of rows chosen in a drop down list

I am trying to learn AngularJS 1.6 and I am trying to populate a table with rows depending on the amount of rows selected in a dropdown list. It could be anywhere from 1 to 12. So far I have the following code.
<body ng-controller="myController">
<div>
<p>Blue Corner Boxer: </p> <input type="text" ng-model="nameBlue">
<br>
<p>Red Corner Boxer: </p> <input type="text" ng-model="nameRed">
<br>
<p>Number of Rounds:</p> <select ng-model="selectedRounds"><option ng-repeat="x in rounds">{{x}}</option></select>
</div>
<div>
<table class="table table-striped">
<thead>
<tr>
<th style="text-align:center">{{nameBlue}}</th>
<th style="text-align:center">Round</th>
<th style="text-align:center">{{nameRed}}</th>
</tr>
</thead>
<tbody class="tablerows">
<tr ng-repeat="x in selectedRounds">
<td>Test</td>
<td>Test</td>
<td>Test</td>
</tr>
</tbody>
</table>
<h2 style="text-align: center">Final Score: </h2> {{scoreBlue1 + ScoreBlue2}}
</div>
</body>
Then in the js file
//Create the module
var myApp = angular.module("myModule", []);
//Create the controller and register the module in one line
myApp.controller("myController", function ($scope) {
$scope.message = "AngularJS tutorial";
$scope.score = [1,2,3,4,5,6,7,8,9,10];
$scope.rounds = [1,2,3,4,5,6,7,8,9,10,11,12];
});
So far the table will add 1 row on selection of anything 1 to 9, but 10 to 12 adds 2 rows. So I think I am wondering how to create an array length of size "selectedrounds" that will repeat rows with the repeater.
Thanks
If you need an array only for iterating and you don't worry about data in it. You can do something like this:
In your controller:
$scope.selectedRounds = 0;
$scope.getRoundsArray(){
return new Array($scope.selectedRounds);
}
We just create an array with needed length and with all elements are 'undefined'. If you create an array with 3 lenght you will get: ['undefined', 'undefined', 'undefined'].
In view:
<tr ng-repeat="x in getRoundsArray() track by $index">
You need this track by $index cause your array contains only 'undefined' values so to prevent angular arguing about duplicate keys we need to use track by.

ng-repeat only repeating once while trying to populate table

I am trying to create a table based on the selection of a drop down list. The user would choose 1 to 12 and that number would be the amount of rows to create in the table. Here is my code so far
<body ng-controller="myController">
<div>
<p>Blue Corner Boxer: </p> <input type="text" ng-model="nameBlue">
<br>
<p>Red Corner Boxer: </p> <input type="text" ng-model="nameRed">
<br>
<p>Number of Rounds:</p> <select ng-model="selectedRounds"><option ng-repeat="x in rounds">{{x}}</option></select>
</div>
<div>
<table class="table table-striped">
<thead>
<tr>
<th style="text-align:center">{{nameBlue}}</th>
<th style="text-align:center">Round</th>
<th style="text-align:center">{{nameRed}}</th>
</tr>
</thead>
<tbody class="tablerows">
<tr ng-repeat="x in getRoundsArray(selectedRounds) track by $index">
<td><select ng-options="x for x in score"></select></td>
<td>Score</td>
<td><select ng-options="x for x in score"></select></td>
</tr>
</tbody>
</table>
var myApp = angular.module("myModule", []);
myApp.controller("myController", function ($scope) {
$scope.message = "AngularJS tutorial";
$scope.score = [1,2,3,4,5,6,7,8,9,10];
$scope.rounds = [1,2,3,4,5,6,7,8,9,10,11,12];
$scope.selectedRounds = 0;
$scope.getRoundsArray = function(rounds){
return new Array(rounds);
}
});
No matter what number I choose in the drop down list it just adds 1 row to the table at a time for each selection I make. Instead I want 6 rows as soon as user selects 6.
You need to re-architect your code slightly.
The repeat needs to be over a scoped variable (tableArray in my example) that changes whenever the dropdown changes.
You also do need the $index in the track for it to work.
Here is a working plunker.
Your function
return new Array(rounds);
has changed to
$scope.tableArray = new Array( $scope.selectedRounds * 1);
$scope.getRoundsArray = function(rounds){
return new Array(rounds);
}
});
this does not do what you think it does. You are calling it as getRoundsArray(6), which creates an array with one entry, which is 6, and not, as you want, an array with six entries.
See, for example, https://www.w3schools.com/js/js_arrays.asp.
Thus, your
<tr ng-repeat="x in getRoundsArray(selectedRounds) track by $index">
repeats for each of the one entries of that array, giving the result that you describe.
(and you don't need that track by)

How to make dynamic content-editable table using angular JS?

Caveat: I've just started with client side scripting and Angular JS is the first thing I'm learning and now I feel I should've started with javascript.
PS: I don't wanna use any third party libraries. I wanna learn to code.
Anyway,I have dynamic table which I want to make editable using content-editable=true attribute of HTML.
Problem: How to I get the edited data? whenever I click on submit and pass the this object to the check() function. I doesn't contain edited values. is there a possible way to pass only edited value if it's dirty. It has pagination so If g to the next page the edited values are gone. I know I've give unique Id to every td element with $Index concatenated to it. But I don't know how should I proceed.
Any help or guidance will be appreciated. Controllers and others are defined in my route.
<div>
<form ng-submit="check(this)">
<table class="table table-striped table-hover">
<tbody>
<tr ng-repeat="data in currentItems">
<td contenteditable="true >{{data.EmpNo}}</td>
<td contenteditable="true">{{data.isActive}}</td>
<td contenteditable="true">{{data.balance}}</td>
<td contenteditable="true">{{data.age}}</td>
<td contenteditable="true">{{data.eyeColor}}</td>
<td contenteditable="true">{{data.fname}}</td>
</tr>
</tbody>
<tfoot>
<td>
<div class="pagination pull-right">
<li ng-class="{'disabled': previousPage}">
<a ng-click="previousPage()" >Previous</a>
</li>
<li ng-repeat="page in pageLengthArray track by $index">
<a ng-click="pagination($index)">{{$index+1}} </a>
</li>
<li disabled="disabled">
<a ng-click="nextPage()" ng-class="{'disabled':nextPage}>Next </a>
</li>
</div>
</td>
</tfoot>
</table>
<input type="submit" value="Submit">
</form>
$scope.currentPage=0;
$scope.pageSize=10;
$scope.currentItems;
$scope.tableData;
$http.get('../json/generated.json').then(function(response){
$scope.tableData=response.data;
$scope.pageLength=Math.ceil($scope.tableData.length/$scope.pageSize);
$scope.currentItems=$scope.tableData.slice($scope.currentPage,$scope.pageSize);
$scope.pageLengthArray= new Array($scope.pageLength);
});
$scope.pagination=function(currentPage){ $scope.currentItems=$scope.tableData.slice($scope.pageSize*currentPage,$scope.pageSize*currentPage+$scope.pageSize);
$scope.currentPage=currentPage;
}
$scope.nextPage=function nextPage(argument) {
$scope.currentPage++; $scope.currentItems=$scope.tableData.slice(($scope.pageSize*$scope.currentPage),($scope.pageSize*($scope.currentPage)+$scope.pageSize));
}
$scope.previousPage=function previousPage(argument) {
$scope.currentPage--;
$scope.currentItems=$scope.tableData.slice(($scope.pageSize*$scope.currentPage),($scope.pageSize*($scope.currentPage)+$scope.pageSize));
}
In the usual case, you can not get a change model for contenteditabe because to change the model used ngModel.
But we can create a directive that we have updated the value of the model.
Live example on jsfiddle.
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope, $timeout) {
$scope.data = {
EmpNo: "123"
};
})
.directive('contenteditable', function($timeout) {
return {
restrict: "A",
priority: 1000,
scope: {
ngModel: "="
},
link: function(scope, element) {
element.html(scope.ngModel);
element.on('focus blur keyup paste input', function() {
scope.ngModel = element.text();
scope.$apply();
return element;
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<table>
<tr>
<td ng-model="data.EmpNo" contenteditable="true"></td>
</tr>
</table>
<pre>{{data|json}}</pre>
</div>
</div>
I would store any object that gets modified in a seperate array using the ng-keyup directive. When the form is submitted, you will have an array of only elements which have been modified. You may have some UX issues if your pagination is done by server as when you change page and come back, it will show your old data, but hopefully this helps.
$scope.check = function () {
// check modifiedItems
console.log(modifiedItems);
};
// store modified objects in a seperate array
var modifiedItems = [];
$scope.modifyItem = function (data) {
// check if data has already been modified and splice it first
for(var i = 0, j = modifiedItems.length; i < j; i++) {
var currentItem = modifiedItems[i];
if (currentItem.id === data.id) {
modifiedItems.splice(i, 1);
break;
}
}
// add to modified
modifiedItems.push(data);
console.log('modifiedItems: ', modifiedItems);
};
HTML
<form ng-submit="check()">
<table class="table table-striped table-hover">
<tbody>
<tr ng-repeat="data in currentItems">
<td ng-repeat="(key, value) in data" contenteditable="true"
ng-keyup="modifyItem(data)">
{{data[key]}}
</td>
</tr>
</tbody>
<tfoot>
</table>
<input type="submit" value="Submit">
</form>

Ng-init if array not yet created

I'm trying to build a template for a application and want to display a dynamic list with names. so i got this code to show the list and add/remove rows;
<table ng-init="page.businessRows = []">
<thead>
<tr>
<th>Company</th>
<th>Contact</th>
<th>Phone</th>
</tr>
</thead>
<tr ng-repeat="row in page.businessRows">
<td>
<input type="text" ng-model="row.name" />
</td>
<td>
<input type="text" ng-model="row.contact" />
</td>
<td>
<input type="text" ng-model="row.phone" />
</td>
<td>
<button ng-click="page.businessRows.splice($index,1)">
Remove
</button>
</td>
</tr>
</table>
<button class="btn" ng-click="page.businessRows.push({})">addRow</button>
the thing as that when this template is loaded page.busnessRows will most likely be loaded with rows so i want to change the ng-init to only create the empty array if businessRows is not initialised.
I have tried ng-init="page.businessRows = page.businessRows.length < 1 ? [] : page.businessRows but it did not work. How am i inteded to do conditions in jsangular expressions?
All help appreciated. Thanks in advance
You can do this instead:
<table ng-init="page.businessRows = page.businessRows || []">
Update
I look at the parser code of AngularJS and notice that version 1.2 (currently RC) supports ternary expression. So if you use AngularJS 1.2, this will also work (although more verbose than the above code):
<table ng-init="page.businessRows = page.businessRows == null ? [] : page.businessRows">
See demo here.
However, your original code might not work if page.businessRows is null, because the parser will fail to dereference length property of null. So just be careful there.
I don't think the ng-init will evaluate conditional statements properly. But you could refactor the condition into a controller function and call the function from ng-init.
<table ng-init="initializeBusinessRows(page.businessRows)">
The just put your conditional evaluation in the function on the controller scope.
I think you're trying to solve the wrong problem.
The problem is that you're allowing an action to occur before the data is loaded or ready. A secondary problem is you're using an expression in an ng-click where a scope function or controller function should be.
So...
Disable that button if the form isn't ready.
Use your controller to control these interactions.
So here's an example of the controller. The $timeout was added to simulate a delayed load of data into your $scope.page variable.
app.controller('MyCtrl', function($scope, $timeout, $window) {
//Timeout to simulate the asynchronous load
//of the page object on the $scope
$timeout(function(){
$scope.page = {
businessRows: []
};
}, 2000);
//scope method to add a row.
$scope.addRow = function (){
//for safety's sake, check to see if the businessRows array is there.
if($scope.page && angular.isArray($scope.page.businessRows)) {
$scope.page.businessRows.push({});
}
};
//scope method to remove a row
$scope.removeRow = function(index, row) {
if($window.confirm('Are you sure you want to delete this row?')) {
$scope.page.businessRows.splice(index, 1);
}
};
});
... and the HTML view (notice the ng-disabled and the ng-click) (and lack of ng-init):
<div ng-controller="MyCtrl">
<table>
<thead>
<tr>
<th>Company</th>
<th>Contact</th>
<th>Phone</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in page.businessRows">
<td>
<input type="text" ng-model="row.name" />
</td>
<td>
<input type="text" ng-model="row.contact" />
</td>
<td>
<input type="text" ng-model="row.phone" />
</td>
<td>
<button ng-click="removeRow($index, row)">
Remove
</button>
</td>
</tr>
</tbody>
</table>
<button class="btn" ng-disabled="!page" ng-click="addRow()">addRow</button>
</div>
Also, here's the obligatory Plunker for you to see this in action.

Categories