How to get notified when grid changed in AngularJS ui-grid? - javascript

If there is a way in ui-grid that I can know a grid is finish updating the rows?(like a filter is being applied etc)? I want to run some function after the grid view changes.
I tried the following method:
$scope.filteredRows = $scope.gridApi.core.getVisibleRows($scope.gridApi.grid);
$scope.$watch('filteredRows', function(){console.log('view updated');});
The above approach works when the grid just finish initiating, after that, it won't work anymore.
I also tried using the filterChanged api:
$scope.gridApi.core.on.filterChanged($scope, function() {
console.log('filter changed');
foo();
});
The problem with this method is that although I can get notified when the filter is changed, but if the grid is very large, it needs some time to finish updating the view, and before that, the function foo() is being called before the grid update is finished.
Any idea will be appreciated.

I've seen use of $scope.grid.api.core.on.rowsRendered( $scope, $scope.col.updateAggregationValue ); in ui-grid-footer-cell.js. I'm not sure exactly when rowsRendered fires, but given it's being used to calculate aggregations and aggregations require knowledge whenever the rows are changed, and must run after the rowsProcessors finish running, there's a good chance that it's what you want.
EDIT: the framework to use it would be:
Define a function that you want to call when the visible rows have changed
var myFunction = function() {
do some stuff
};
Set this function to be called whenever rows are rendered
$scope.gridApi.core.on.rowsRendered( $scope, myFunction );

Well, I found a workaround, in order to call the function after the grid is updated, which takes some time, I added a delay in filterChanged event:
$scope.gridApi.core.on.filterChanged($scope, function() {
console.log('filter changed');
$timeout(foo(),800);
});
To use the $timeout, you need to add that to your controller first.

Related

How to use the Firebase real time database for web application

In my website I'm Showing my database after user has given the database name, Is there any way I can constantly update the web shown databasebase without refreshing the page . I've tried using setInterval but it's not working for some reason .
function c(){
setInterval(beta, 1000);
}
function beta(){
var d = document.getElementById("opopo").value;
var firebaseRefff= firebase.database().ref('LOCATION/'+d);
firebaseRefff.on('child_added', snap=> {
var slot=snap.getKey();
var alloted=snap.child("ALLOTED").val();
var date=snap.child("DATE").val();
var limit=snap.child("LIMIT").val();
var time=snap.child("TIME").val();
$("table tbody").append(""+slot+""+alloted+""+date+""+limit+""+time+"Null");
});
}
You do not need, and should not use, setInterval to trigger the queries. What you have in your beta() function looks pretty good.
firebaseRefff.on('child_added', snap => {}) means "whenever a child is added under this location, trigger the callback function (empty in my example) with the parameter 'snap'". It will also be called once, initially, for each child that is already at that database reference location.
You need to make sure you've called beta() once to setup this trigger.
If you're still having problems, you might want to insert logging to make sure beta() is being called, what the full reference path is, if the callback is ever triggered, and if your jquery string is correct.

Manually entering text in angular-ui

My question comes from the selected answer here
<ui-select-choices repeat="hero in getSuperheroes($select.search) | filter: $select.search">
<div ng-bind="hero"></div>
works perfectly fine with:
$scope.getSuperheroes = function(search) {
var newSupes = $scope.superheroes.slice();
if (search && newSupes.indexOf(search) === -1) {
newSupes.unshift(search);
}
return newSupes;
}
However, when I put a breakpoint on $scope.superheroes I see its called as many times as my data. Data is very large. Its fetched from a $http.get() request and I'm handling not loading the data in the textbox all at once.
However, I don't want to call this until I actually start typing anything in the textbox. I tried to call getSuperheroes using
on-select="getSuperheroes($select.search)"
and similarly
refresh="getSuperheroes($select.search)"
but they do not help allowing manual entry of data.
What do I use to call the function and yet get the task done just like how it works in the referenced answer?
I recommend you to do this:
In order to improve performance get rid of repeat="hero in getSuperheroes($select.search) | filter: $select.search", since if your data doesn't change the function getSuperheroes will be triggered (anyway) every time a $digest is executed (no matter the var newSupes changes or not in getSuperheroes) (that's wahat's happening right now!). Instead put (for instance) repeat="hero in myHeroes" and then in your getSuperheroes func do this:
$scope.getSuperheroes = function(search) {
var aux = $scope.superheroes.slice();
if (search && aux.indexOf(search) === -1) {
aux.unshift(search);
}
$scope.myHeroes = aux;
}
Keep the refresh="getSuperheroes($select.search)", this will search the new values by calling getSuperheroes which will update the $scope.myHeroes var and then it will be refreshed in the view.
Add the property refresh-delay="500" (500 is just an example, put whatever fits your needs). This will delay the search that amount of milisecond. This is useful when you do not want start searching immediately. For instance if the user wants to search "Superman" and she/he type "Sup" in less than 500 milisec the search will start with the string "Sup". If you do not set that property the search would be executed 3 times (one for "S" another for "Su" and a third one for "Sup")
PS:
-- If you want to get the ui-select with some values initially, You must call the getSuperheroes func on your controller start.
-- You may need to declare $scope.myHeroes = []; at the beginning of your controller (it depends on your controller implementation).
-- You might want to read more info about $digest on the AngularJS official doc, on SO there are some related posts:
How exactly does the AngularJS Digest Loop work?
Why do i have to call $scope.$digest() here?
Angular $scope.$digest vs $scope.$apply
$apply vs $digest in directive testing

ExtJS 4 grid and some problems with mask

I have a not too big grid (30x20) with numbers in cells. I have to display all, calculate them in different ways (by columns, rows, some cells, etc.) and write values to some cells. This data is also written and read from db table fields. Everything is working, excluding simple (theoretically) mask tools.
In time of e.g. writing data to the field in the table I try to start mask and close it on finish. I used such a “masks” very often but only in this situation I have a problem and can’t solve it.
I prepare this mask the following way:
msk = new Ext.LoadMask(Ext.getBody(), { msg: "data loading ..." });
msk.show();
[writing data loops]
msk.hide();
msk.destroy();
I also tried to use grid obiect in place of Ext.getBody(), but without result.
I found also that the program behaves in a special way – loops which I use to write data to the table field are "omitted" by this mask, and it looks like loops are working in the background (asynchronously).
Would you be so kind as to suggest something?
No, no, no, sorry guys but my description isn’t very precise. It isn’t problem of loading or writing data to the database. Let’s say stores are in the memory but my problem is to calculate something and write into the grid. Just to see this values on the screen. Let me use my example once again:
msk = new Ext.LoadMask(Ext.getBody(), { msg: "data loading ..." });
msk.show();
Ext.each(dataX.getRange(), function (X) {
Ext.each(dataY.getRange(), function (Y) {
…
X.set('aaa', 10);
…
}
msk.hide();
msk.destroy();
And in such a situation this mask isn’t visible or is too fast to see it.
In the mean time I find (I think) a good description of my problem but still can’t find a solution for me. When I use e.g. alert() function I see this mask, when I use delay anyway, mask is too fast. Explanation is the following:
The reason for that is quite simple - JS is single threaded. If you modify DOM (for example by turning mask on) the actual change is made immediately after current execution path is finished. Because you turn mask on in beginning of some time-consuming task, browser waits with DOM changes until it finishes. Because you turn mask off at the end of method, it might not show at all. Solution is simple - invoke store rebuild after some delay.*
I have no idea how is your code looks in general but this is some tip that you could actually use.
First of all loading operations are asynchronously so you need to make that mask show and then somehow destroy when data are loaded.
First of all check if in your store configuration you have autoLoad: false
If yes then we can make next step:
Since Extjs is strongly about MVC design pattern you should have your controller somewhere in your project.
I suppose you are loading your data on afterrender or on button click event so we can make this:
In function for example loadImportantData
loadImportantData: function(){
var controller = this;
var store = controller.getStore('YourStore'); //or Ext.getStore('YourStore'); depends on your configuration in controller
var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
myMask.show();
store.load({
callback: function (records, operation, success) {
//this callback is fired when your store load all data.
//then hide mask.
myMask.hide();
}
});
}
When data is loaded your mask will disappear.
If you have a reference to the grid, you can simply call grid.setLoading(true) to display a loading mask over the grid at any time.

Wait in angular until elements are loaded?

In angular we have an enrich method which runs some rest call to enrich a data object, then sets a variable which will cause a hidden details tab to be visible. Something like this overly simplified example:
$scope.enrich = function(team){
angular.forEach(team.members, function(member){
member.getSkills().then(function(skills){
member.skills=skills;
}
});
$scope.enrichFinished=true;
};
I'm getting exceptions in the detail pane which is opened when enrichFinished is true. The exceptions appear to be due to a filter which attempts to filter on member.skill and discovers skill is undefined for the member. I assume the problem is that we open the detail tab as soon as enrichFinished is set, which is before the then clause that sets member.skills=skill; thus we have a datarace where skills's may not yet be set by the time we try to filter on it.
What is the cleanest way to tell angular to wait to run the filter in the detail's tab until after I have actually generated and saved the data I need? The actual enrich method enriches 3-4 different variables within the for loop.
You can set $scope.enrichFinished=true; after all promisses solve, so you can use $q.all like this
$scope.enrich = function(team){
$q.all(team.members.map(
function(member){
return member.getSkills().then(
function(skills){
member.skills=skills;
}
);
}
)
).then(function(){
$scope.enrichFinished=true;
});
};

ng-repeat not growing with object

I'm doing this:
<body>
<div ng-controller="PresentationCtrl">
Find
<div>
<ul class ="unstyled">
<li ng-repeat="p in presentations">
<img ng-src="{{p}}" alt="presentation">
</li>
</ul>
<div>
</div>
</body>
I have one placeholder element inside of presentations that is set when the function PresentationCtrl is hit.
When the findAll link is hit, I add an element to the array like so: $scope.presentations.push(sUrl);
But, when viewing my page, the list doesn't grow.
Does ng-repeat only fire once, or something? Can I force it to "refresh" and display the current values in presentations?
Here's my controller
The console.log before I push the element into the array gets hit. and displays the string that I expect.
function PresentationCtrl($scope){
$scope.presentations = ["http://angularjs.org/img/AngularJS-small.png"];
$scope.findAll = function(){
var socket=null;
socket = io.connect('http://[server]:3000');
socket.on('handshake',function(){
socket.emit('viewAll',{tenant:'qa'});
socket.on('returnAll',function(back){
for(i=0;i<back.length;i++){
for(j=0;j<back[i].slides.length;j++){
socket.emit('signUrl',(back[i].slides[j].location));
break;
}
}
socket.on('signedUrls',function(sUrl){
console.log("back from signedUrls" + sUrl);
$scope.presentations.push(sUrl);
});
});
});
};
}
it works fine.
http://plnkr.co/edit/agadSBFgz8YhWuDTtCPx?p=preview
Your situation might be different.
--------- EDIT ---------
ok, I got your situation. You are running a function not under watch of AngularJS.
Thus, AngularJS does not know that your variable is changed, basically $digest is not called.
To mimic native Javascript asynchronous call, I use setTimeout.
The following code does not work. It change the value, but it won't be watched.
setTimeout( function() {
$scope.presentations = [6,7,8,9];
}, 1000);
However this code does work. It change the value as expected
$timeout( function() {
$scope.presentations = [6,7,8,9];
}, 1000);
The difference between setTimeout and $timeout is that $timeout is running under watch of AngularJS.
Thus to make it work, you need to run $scope.apply() after it, or within it as a function.
This code does work with setTimeout.
setTimeout( function() {
$scope.presentations = [6,7,8,9];
$scope.$apply();
}, 1000);
Can't explain all to you in detail because I don't really mastered the AngularJS code.
This blog has a very good explanation about why we need to run $apply.
http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
Just for the tip, to get the answer quickly, it's better to have a simplified example in jsfiddle or plunkr.
I know $http call cannot be demoed in plunkr or jsfiddle, but you can mimic your situation using setTimeout. It can make your situation understandable to readers most of time.

Categories