I have an HTML file which displays a data set in firebase as a table. As the data is in firebase I am using (key,client) in clientInfo to get the key as well. This key is used for the purpose of deleting a row.
<table class="meeting">
<tr ng-repeat="(key, client) in clientInfo">
<td>{{ client.name }}</td>
<td>{{ client.discount }}</td>
<td>{{ client.vms }}</td>
<td><button class="btn btn-delete tooltip" ng-click="deleteClient(key)">
<span>Delete this client</span></button>
</td>
</tr>
</table>
But while clicking the button, the deleteClient(key) function is not called. Is it because the information of key isnt available on a Cell(td) level and is available only on a Row(tr) level in the HTML?
I am using an older version of Firebase where $as.Object() is required to store data as an Object.
My controller in Angular
myApp.controller('DataController', function ($scope, $rootScope, $firebase, FIREBASE_URL) {
var ref = new Firebase(FIREBASE_URL + '/clientInfo');
var clientInfo = $firebase(ref);
$scope.clientInfo = clientInfo.$asObject();
$scope.deleteClient = function(key) {
clientInfo.$remove(key);
};
Is it possible to do this in Table format? If so, what am I doing wrong? Please help me solve this issue
Related
I have got code that reads the data from the array perfectly when I use a AJAX request. When I push an object to the array however, ng-repeat doesn't render the new row and I have to refresh the page to then fetch the data that was sent to server.
Why does it do this?
Thanks
Javascript
function processError() {
var responseCode = 404;
var error = {};
error["client"] = document.getElementById('client').value;
error["errorMessage"] = document.getElementById('error-message').value;
error["simpleRes"] = document.getElementById('simple-fix').value;
error["fullRes"] = document.getElementById('full-fix').value;
error["reason"] = document.getElementById('reason').value;
var errorJson = JSON.stringify(error);
$.ajax({
url: "../ErrorChecker/rest/error",
type: "POST",
data: errorJson,
contentType: "application/json"
})
.done(function (data, statusText, xhr, displayMessage) {
$('.form').hide();
responseCode = xhr.status;
reloadData(data);
});
function reloadData(data) {
if (responseCode == 200) {
processPositiveResponse(data);
} else {
$('#negative-message').show(1000);
}
}
}
function processPositiveResponse(data) {
$('#positive-message').show(1000);
updateTable(data);
$('#errorTable').DataTable().destroy();
setupTable();
clearInputs();
console.log($scope.controller.errors);
}
function updateTable(data) {
$scope.controller.errors.push({
"id": data,
"client": document.getElementById('client').value,
"errorMessage": document.getElementById('error-message').value,
"simpleRes": document.getElementById('simple-fix').value,
"fullRes": document.getElementById('full-fix').value,
"reason": document.getElementById('reason').value
})
}
HTML
<tbody>
<tr ng-repeat="x in dataCtrl.errors">
<td class="collapsing">
<div class="ui toggle checkbox">
<input type="checkbox">
<label></label>
</div>
</td>
<td style="display: none">{{ x.id }}</td>
<td>{{ x.client }}</td>
<td>{{ x.errorMessage }}</td>
<td>{{ x.simpleRes }}</td>
<td>{{ x.fullRes }}</td>
<td>{{ x.reason }}</td>
</tr>
</tbody>
That's because you're using jQuery and Angular together. Don't do that. EVER. Angular is not aware of what jQuery is doing, and jQuery is not aware of what Angular is generating in the DOM. Solution : REMOVE jQuery and use Angular's own $http service.
The same way, don't use document.getElementById('full-fix').value. You're taking Angular backwards. Angular generates the DOM from data, so you don't need to select DOM elements to read their value because that value is already in your data.
Update the document. Use scope apply as a quick fix. Not the way to do it imo. But it will solve your problem fast. On my mobile if I do not forget ill update this comment later with more details and best practises. For now a google search to scope apply will help you a long way.
I am developing my first angular-app with an .Net backend.
I get my data async from a webmethod using a http.post. That all works fine.
Client-side I would like to do some simple calculations (a final row in a table which contains sums of all the data in table)
The code to do this is pretty straight forward but my problem is the data i not ready when I try to do it.
I have read that I could use a promise and a service or a factory. But I am not sure what we be the best way to go.
My code for the view:
<div ng-controller="taskCtrl as ctrl">
<div class="col-md-10 container outer">
<h1 class="center-block">{{ctrl.SprintViewModel.SprintName}}</h1>
<table id="SprintMetaDate">
<tr><td>Projekt:</td><td>{{ctrl.SprintViewModel.ProjektName}}</td></tr>
<tr><td>Periode:</td><td>{{ctrl.SprintViewModel.StartDate}} - {{Ctrl.SprintViewModel.EndDate}}</td></tr>
<tr><td>Udarbejdet af/d:</td><td>{{ctrl.SprintViewModel.MadeBy}}</td></tr>
</table>
<h3>Sprint Resume:</h3>
<br/>
{{ctrl.SprintViewModel.SprintResume}}
<h3>Sprint afslutning:</h3>
{{ctrl.SprintViewModel.SprintDemo}}
<h2>Scope og Økonomi </h2>
<h3>Sprint Opgaver</h3>
<table id="SprintTasks" class="col-md-12">
<tr><th>Opgave</th><th>Estimat</th><th>Forbrug</th><th>Udest.</th><th>*</th><th>Pris (DKK)</th></tr>
<tr ng-repeat="x in ctrl.SprintViewModel.Tasks">
<td style="width: 40%">{{ x.Description }}</td>
<td>{{ x.TimeEst }}</td>
<td>{{ x.TimeUsed }}</td>
<td>{{ x.TimeRemaining }}</td>
<td>{{ ctrl.CalcPrecisionOfEstimat(x.TimeUsed,x.TimeRemaining,x.TimeEst) | number:2}} %</td>
<td>{{x.Price}}</td>
</tr>
<tr>
<td>Ialt</td>
<td>{{ ctrl.TotalEstimat() }}</td>
<td>{{ ctrl.TotalTimeUsed() }}</td>
<td>{{ctrl.TotalTimeRemaining()}}</td>
<td>{{ctrl.TotalPrecision()}}</td>
<td>{{ctrl.TotalPrice()}}</td>
</tr>
</table>
* Forbrug + Udestående i forhold til estimat
<br/>
Udestående opgaver er planlagt ind i næstkommende sprint.
</div>
</div>
</form>
<script>
var app = angular.module('myApp', []);
app.controller('taskCtrl', function($scope, $http) {
var ctrl = this;
ctrl.SprintViewModel = null;
ctrl.TotalEstimat=function() {
var totalEstimat=0;
for (i=0; i<ctrl.SprintViewModel.Tasks.count;i++) {
totalEstimat += ctrl.SprintViewModel.Tasks[i].Estimate;
}
return totalEstimat;
}
ctrl.TotalPrecision = function () {
var totalPrecision=0;
angular.forEach(ctrl.SprintViewModel.Tasks, function (value, key) {
totalPrecision += Number(value);
});
$http.post('SprintRapport.aspx/GetSprintViewModel', {})
.then(function(response, status, headers, config) {
console.log("I success");
ctrl.SprintViewModel = response.data.d;
});
});`
As already mentioned I get a nullreference every when the page-load on all the methods in the last row, because ctrl.SprintviewModel is undefined. I have only included one of the methods for simplicity, the problem is the same for all of them.
So my question is how do I make sure that ctrl.TotalEstimat() first get called then ctrl.SprintViewModel is assigned?
You can add ng-if condition to the last <tr> which resolves to true when data is ready to populate in your controller. So you can define $scope.loading = false initially and once your code is ready to populate you set $scope.loading=true and that will call $digest cycle internally and your view gets updated.
There are several things that you could do. I've fixed this kind of issue by placing guard conditions in the functions. These check that the necessary variables have been set before continuing. So adding if (!ctrl.SprintViewModel) return; at the beginning of the function as follows:
ctrl.TotalEstimat=function() {
// Guard Condition to prevent function executing in invalid state.
if (!ctrl.SprintViewModel) return;
var totalEstimat=0;
for (i=0; i<ctrl.SprintViewModel.Tasks.count;i++) {
totalEstimat += ctrl.SprintViewModel.Tasks[i].Estimate;
}
return totalEstimat;
}
It's another option, but as you have already alluded to, I think that promises and the $q library is the proper angular way to fix this sort of thing.
I know there are many questions already posted for the same issue but none of the solutions work in my case.
On calling a web service I get JSON response. In this JSON, there are around 2000+ objects from which I need to display data on the table. I want to display all (2000+) records in the table and Yes, I cannot limit or paginate, need to display it on a single page (I know it's stupid but it's the business requirement). I don't need sorting or searching.
Data transfer is about 2MB and the request completes in about 2-4 secs approx. but it takes around 10-15 secs to data render on the page.
Now, what I am looking for is either speed ng-repeat binding things up (if possible) or display the data as soon as I receive it and keep on adding it until all rows are displayed.
Check out the code below :
HTML
<table class="table table-bordered table-striped cf">
<thead style="color: #333;">
<tr>
<td>Asset Name</td>
<td>Date/ Time</td>
<td>Location</td>
<td>Ignition</td>
<td>Speed</td>
<td>Heading</td>
<td>Direction</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="cols in tableData">
<td>{{ cols.aN }}</td>
<td>{{ cols.dT }}</td>
<td>{{ cols.Lat }}, {{ cols.Lon }}</td>
<td>{{ cols.I }}</td>
<td>{{ cols.S }}</td>
<td>{{ cols.H }}</td>
<td>{{ cols.D }}</td>
</tr>
</tbody>
</table>
JS
var ignition_text = '';
var lat = '';
var lon = '';
for (var i = 0; i < data.length; i++) {
if (data[i].ignition = 1) {
ignition_text = "On";
} else {
ignition_text = "Off";
}
$scope.$apply(function() {
$scope.tableData.push({
aN: name,
dT: data[i].eventUTCTime,
Lat: data[i].latitudeDegrees,
Lon: data[i].longitudeDegrees,
I: ignition_text,
S: data[i].speedMPH,
H: data[i].longitudeDegrees,
D: data[i].latitudeDegrees
});
});
}
Thanks in advance!
You probably wont need $scope.$apply at all. And even if you need it, you should only use it once you pushed all data to the table. Otherwise, every added entry will force an digest-cycle. Just build your array and assign the finished array to the scope-variable. Then angular will only build the table once.
Depending on the nature of your variable name you may be able to eliminate the array building as well and just use the data you are downloading. Apart from nameyou just use that data anyway.
Here is a plunk that has a similar data size but loads much faster http://plnkr.co/edit/I4rN1ZMaR3e1mbcsJ9Ka. If you were to make a quick plunk I could use your data and edit your code but from the looks you just need the main assignment to the scope without the apply for the data and add a track by to the ng-repeat. SN: You would want to manipulate your data inside the for loop then do the assignment to the scope.
for (var i = 0; i < data.length; i++) {
if (data[i].ignition = 1) {
ignition_text = "On";
} else {
ignition_text = "Off";
}
}
$scope.tableData=data;
JS
$http.get("largeData.json").then(function(response) {
vm.test = response.data;
});
HTML
<tbody>
<tr ng-repeat="(key, value) in main.test track by $index ">
<td>{{ value.ask }}</td>
<td>{{ value.bid }}</td>
<td>{{ value.volume_btc }}, {{ value.volume_percent }}</td>
<td>{{ value.last }}</td>
<td>{{ value.timestamp }}</td>
</tr>
</tbody>
I am building an application using ASP.NET and AngularJs. I am trying to pass object id from the view (HTML) to the Angular controller, to then make an http call to the api. I don't know how to pass the id of an specific object from this table when the delete link is clicked.
<table class="table table-responsive table-striped">
<tr ng-repeat="post in viewModel.posts">
<td>{{ post.text }}</td>
<td>{{ post.postedOn | date:'medium' }}</td>
<td>Edit</td>
<td><a ng-click="viewModel.deletePost" class="btn btn-sm">Delete</a></td>
</tr>
</table>
and this is how I am trying to make the call to the API from the controller
// delete post function
viewModel.deletePost = function () {
viewModel.errorMessage = "";
$http.delete("http://localhost:61878/api/posts", viewModel.newPost.id)
.then(function (response) {
// sucess delete
viewModel.posts.delete(response.data);
viewModel.newPost = {};
}, function () {
// failed to delete
viewModel.errorMessage = "Failed to delete post"
})
.finally(function () {
});
};
Does anybody know how can I pass the id of the respective post on the row clicked to the Angular controller?
viewModel.deletePost is a function - just call it with parameters.
<a ng-click="viewModel.deletePost(post.id)" class="btn btn-sm">Delete</a>
After that you just have to add the parameter to the function:
viewModel.deletePost = function (id) { ... }
Consider this conroller
$scope.transaction = {};
$scope.transactions = Transaction.query();
$scope.save = function() {
var transaction = new Transaction();
transaction.name = $scope.transaction['name'];
transaction.debit = $scope.transaction['debit'];
transaction.date = $scope.transaction['date'];
transaction.amount = $scope.transaction['amount'];
transaction.category = $scope.transaction['category'].uuid;
//noinspection JSUnresolvedFunction
transaction.$save();
$scope.transactions.push(transaction);
console.log('transaction saved successfully', transaction);
};
and this HTML
<tbody ng-repeat="transaction in transactions | orderBy: transaction.created_on">
<td>{{ transaction.name }}</td>
<td>{{ transaction.amount | currency }}</td>
<!-- custom filter to display type of transaction -->
<td>{{ transaction.debit | transactionType }}</td>
<!-- added dateInMillis to pass to date to filter Angular way -->
<td>{{ transaction.created_on | dateInMillis | date: 'medium'}}</td>
<td>{{ transaction.category.name }}</td>
<td>
</tbody>
Problem
When I add transaction, it immediately displays bunch of NaNs and then once the server comes back with saved data, it replaces those NaNs with actual data
How can I prevent that from happening? Its not a good UX
Without seeing all the code related to the Transaction object its hard to know for sure what the problem could be. At a glance I think you need a callback function attached to transaction.$save() method.
transaction.$save(function(u, putResponseHeaders) {
// This is $save's success callback
$scope.transactions.push(transaction);
console.log('transaction saved successfully', transaction);
});