ng-grid : Why is afterSelectionChange() called twice? - angularjs

I did not like the answer to this question. To my mind, it has a bad code smell.
I am new to Angular, so I thought I could learn something if I created a Plunk to help that questioner.
My Plunk is http://plnkr.co/edit/fJcew7cGCPzJ0nhIQXjg?p=preview, but there two things which I don't understand.
Please note that I assigned the ng-grid's data two ways - at one fell stroke and row by row. Comment one out & uncomment the other to play around with it.
And be sure to press F12 to see the developer's console log messages.
1) why am I getting TWO ngGridEventData events?
2) why, when I push() row by row am I not getting one ngGridEventData event per push() ?
I am trying to implement master/detail/even more detailed with 3 grids (nothing to do with the current question) and have been googling for a few days now. Many people seem believe that they will get multiple ngGridEventData events and want to know ho to detect that the grid is fully rendered (hence the check in my Plunk).
In short, can anyone explain in detail how ngGridEventData works? Thanks; your answer will help a lot of people.

In a recent Answer here I showed some learnings about this strange behavior. It seems that this routine is called 2 times for reasons I can't really understand (caching maybe?).
You can get this to work if you only react when the data-object is a clone:
if (data.selected === true) {
if (data.isClone) {
numSelectionChanges = numSelectionChanges + 1;
console.log("afterSelectionChange", 'Selection has chanegd ' + numSelectionChanges + ' times');
console.table(data);
}
}
Give it a try, and if you find out why this happens please let me know.
More findings update:
Have a look at this Plunker
I have included the ng-grid.js script directly in the plunker.
If you look at rows 3344-3349 you will find this piece of code:
$scope.$on('$destroy', $scope.$parent.$watch(options.data, dataWatcher));
$scope.$on('$destroy', $scope.$parent.$watch(options.data + '.length', function() {
dataWatcher($scope.$eval(options.data));
$scope.adjustScrollTop(grid.$viewport.scrollTop(), true);
}));
As you can see the there are two watchers applied which looks like a leftover from changes to me. (Although i'm not sure, with me being dumb and those guys being clever). However, if I comment the first line out everything seems to work as expected.
Be carefull when you try this out, because it may break other functionality. I'm not very comfortable with hacking in such a lengthy source code. If someone who is an actual ng-grid team member sees this he can tell me if I found a bug and how to report it. (Never done that before!)

Related

Making an angular statusbar directive

im looking to implement a directive to display status messages in my ionic angular app.. the idea is that i define a bunch of standard status messages in my template as follows and it's inspired by the stock ng-switch directive..
<status-bar code="statusCode" onShow="onStatusShow" onHide="onStatusHide">
<status-message when-code="OK" style="calm" timeout="3000">My HTML message</status-message>
...
...
<status-message when-complex style-field="style" text-field="text" timeout-field="timeout" />
</status-bar>
my requirements are these:
status-bar
the directive should bind to $scope.statusCode and depending upon its string value, it should activate one of the sub-directives except the when-complex one..
however, if i assign an object to $scope.statusCode, it should activate the when-complex directive if defined..
the directive also exposes an onShow and onHide callbacks..
when changing the value of $scope.statusCode, the previously active sub-directive should be completely hidden before showing the newly active one.. (animations)
status-message
style and timeout attributes are optional and will default to 'stable' and null respectively..
the timeout attribute will cause this sub-directive to show for a short time before clearing $scope.statusCode..
whereas i can write very simple directives, this one is proving to be a bit beyond me.. ive seen the source of ng-switch and its confusing.. i have tried myself as well but i havent gotten really far with this no matter how much ive tried.. im not posting my code approaches here not for the lack of trying but for the sake of cluttering and relevance..
so i was wondering if maybe someone could come up with a possible basic approach on codepen or plunkr that i can use as a base for expanding upon (since this is just a simplified explanation of what i intend to do with this directive).. or atleast point in the directions i need to go in..
after a night of brain-storming and coding punctuated by coffee and smoke breaks.. ive managed to make it work.. once again keeping ng-switch as a base.. the code is a bit long.. and i changed a few requirements along the way for better usability.. and some requirements like #2 and #3 dont work yet.. but im pretty sure ill make it work as well..
so if anybody is having a similar issue or is interested in my solution.. i can post it here.. :)

Nothing happening when I click Post Comment button (ThinksterIO:Real Time Webapps tutorial)

I ran into another snag while going through the ThinksterIO Learn to Build Real Time Webapps tutorial around the 90% mark where one is shown how to add and delete comments:
https://thinkster.io/angulartutorial/learn-to-build-realtime-webapps/#adding-comments-functionality-to-the-post-service-9
Every time I click the Post Comment button absolutely nothing happen and I simply can't figure it out. I am expecting some kind of error to show up but nil. It's as if the button is not hooked up to the function. I was so frustrated that I eventually just copy and pasted the code from the tutorial to make sure I had it down right. Even after doing that it still didn't work.
I've created a Plunker with this app running over here http://embed.plnkr.co/OhzDTU/preview
You can sign in with the email user#user.com and 1234 password. You will then be able to try to comment on posts. Any ideas on what is going wrong here?
UPDATE
I took away the user user#user.com and 1234 password since problem is now fixed.
Your add comment button is in showpost.html, which is rendered by PostViewCtrl. However, your addComment method is attached to the scope of PostsCtrl (it doesn't exist when you try to click it). The simplest answer here is just to move addComment() over to the other controller.
Regarding the lack of error messages, I don't think that you can get an error for this. I had a look at the docs for $log and tried to decide if you could add some debugging output, but it looks like it's already on (i.e. the lack of a method seems to fail silently).
About your only option here is to try something like {{addComment|json}} in the view to see if it exists, but you'd first have to suspect that it didn't.

Best open-source grid with smooth, infinite scrolling

When I started working on my current project I was given quite an arduous task - to build something that in essence suppose to replace big spreadsheet people use internally in my company.
That's why we I thought a paginated table would never work, and quite honestly I think pagination is stupid. Displaying dynamically changing data on a paginated table is lame. Say an item on page #2 with next data update can land on page whatever.
So we needed to build a grid with nice infinite scroll. Don't get me wrong, I've tried many different solutions. First, I built vanilla ng-repeat thing and tried using ng-infinite-scroll, and then ng-scroll from UI.Utils. That quickly get me to the point where scrolling became painfully slow, and I haven't even had used any crazy stuff like complicated cell templates, ng-ifs or filters. Very soon performance became my biggest pain. When I started adding stuff like resizable columns and custom cell templates, no browser could handle all those bindings anymore.
Then I tried ng-grid, and at first I kinda liked it - easy to use, it has a few nice features I needed, but soon I realized - ng-grid is awful. Current version stuffed with bugs, all contributors stopped fixing those and switched to work on a next version. And only God knows when that will be ready to use. ng-grid turned out to be pretty much worse than even vanilla ng-repeat.
I kept trying to find something better. trNgGrid looked good, but way too simplistic and doesn't offer features I was looking for out of the box.
ng-table didn't look much different from ng-grid, probably it would've caused me same performance issues.
And of course I needed to find a way to optimize bindings. Tried bind-once - wasn't satisfied, grid was still laggy. (upd: angular 1.3 offers {{::foo}} syntax for one-time binding)
Then I tried React. Initial experiment looked promising, but in order to build something more complicated I need to learn React specifics, besides that thing feels kinda non-anguleresque and who knows how to test directives built with angular+react. All my efforts to build nice automated testing failed - I couldn't find a way to make React and PhanthomJS to like each other (which is probably more Phantom's problem. is there better headless browser) Also React doesn't solve "appending to DOM" problem - when you push new elements into the data array, for a few milliseconds browser blocks the UI thread. That of course is completely different type of problem.
My colleague (who's working on server-side of things) after seeing my struggles, grumbled to me that I already spent too much, trying to solve performance problems. He made me to try SlickGrid, telling me stories how this is freakin zee best grid widget. I honestly tried it, and quickly wanted to burn my computer. That thing completely depends on jQuery and bunch of jQueryUI plugins and I refuse to suddenly drop to medieval times of web-development and lose all angular goodness. No, thank you.
Then I came by ux-angularjs-datagrid, and I really, really, really liked it. It uses some smart bad-ass algorithm to keep things very responsive. Project is young, yet looks very promising. I was able to build some basic grid with lots of rows (I mean huge number of rows) without straying too much from the way of angular zen and scrolling still smooth. Unfortunately it's not a complete grid widget solution - you won't have resizable columns and other things out of the box, documentation is somewhat lacking, etc.
Also I found this article, and had mixed feelings about it, these guys applied a few non-documented hacks to angular and most probably those would breaks with feature versions of angular.
Of course there are at least couple of paid options like Wijmo and Kendo UI. Those are compatible with angular, however examples shown are quite simple paginated tables and I'm not sure if it is worth even trying them. I might end-up having same performance issues. Also you can't selectively pay just for the grid widget, you have to buy entire suite - full of shit I probably never use.
So, finally to my question - is there good, guaranteed, less painful way to have nice grid with infinite scrolling? Can someone point to good examples, projects or web-pages? Is it safe to use ux-angularjs-datagrid or better to build my own thing using angular and react? Anybody ever tried Kendo or Wijmo grids?
Please! Don't vote for closing this question, I know there are a lot of similar questions on stackoverflow, and I read through almost every single one of them, yet the question remains open.
Maybe the problem is not with the existing widgets but more with the way you use it.
You have to understand that over 2000 bindings angular digest cycles can take too long for the UI to render smoothly. In the same idea the more html nodes you have on your page, the more memory you will use and you might reach the browser capacity to render so many nodes in a smooth way. This is one of the reason why people use this "lame" pagination.
At the end what you need to achieve to get something "smooth" is to limit the amount of displayed data on the page. To make it transparent you can do pagination on scroll.
This plunker shows you the idea, with smart-table. When scrolling down, the next page is loaded (you will have to implement the previous page when scrolling up). And at any time the maximum amount of rows is 40.
function getData(tableState) {
//here you could create a query string from tableState
//fake ajax call
$scope.isLoading = true;
$timeout(function () {
//if we reset (like after a search or an order)
if (tableState.pagination.start === 0) {
$scope.rowCollection = getAPage();
} else {
//we load more
$scope.rowCollection = $scope.rowCollection.concat(getAPage());
//remove first nodes if needed
if (lastStart < tableState.pagination.start && $scope.rowCollection.length > maxNodes) {
//remove the first nodes
$scope.rowCollection.splice(0, 20);
}
}
lastStart = tableState.pagination.start;
$scope.isLoading = false;
}, 1000);
}
This function is called whenever the user scroll down and reach a threshold (with throttle of course for performance reason)
but the important part is where you remove the first entries in the model if you have loaded more than a given amount of data.
I'd like to bring your attention towards Angular Grid. I had the exactly same problems as you said, so ended up writing (and sharing) my own grid widget. It can handle very large datasets and it has excellent scrolling.

AngularJS and ui-sortable(branch 1.2): ng-mouseover breaks after sorting

I upgraded my App to AngularJS 1.2 and so also switched to ui-sortable v 1.2.
The sorting is implemented for Accordion-Groups (from ui-bootstrap). With the master-tree version of sortable i could listen to ng-mouseover/ng-mouseleave inside the accordion headers but with the 1.2 version, the mouseevents are only listening as long as i haven't done any sorting. After performing any change to the sortorder, the mouseevents become deaf...
Here's a Plunker: http://plnkr.co/edit/n8yms9pb7uJp77zZ9LFK?p=preview
Can anybody give me some advice how to fix that?
Thank you
Identity Problem.
elementInsertedByDropping !== elementSelectedAndDragged
In the console, one can verify the assertion above. So that narrows down the category of problem to a relatively familiar one.
I'm learning Angular myself, and I'm also having trouble with ui-sortable; please don't regard my opinions as definitive. However, I believe that the problem is that the $watch listeners need to be re-bound to the new element, as it is being created asynchronously outside of Angular.
The "ng.$rootScope.Scope" documentation describes this situation somewhat clearly in the $apply section. If I am correct, you would need to either $scope.$apply(...) code in your controller, or [preferably] write a custom directive that handles the insertion.
Fortunately, it seems that jQuery-ui-sortable's "update" event can be easily used in a custom directive to ensure that the element is bound. I found that bloggers respectTheCode and Michal Ostruszka discuss the problem of writing jQuery-ui-sortable directives in fairly clear terms; so does a fellow named Greg Gigon and several others, but I'm only allowed to offer you two links at this point.
If I can provide more precise information at a later point, I will revise this answer; I'm still learning this stuff myself, and I would like to know how to do something quite similar.
[edit: I'm not familiar enough with Angular-UI-Sortable to know whether this is a bug or simply missing functionality.]
Looks like a bug in ui-sortable.
My guess: It seems to be losing the bindings from the event directives, probably because it's destroying the old DOM elements and creating a new ones without re-attaching the scope with $compile. I'd save this plunk and submit and issue on their GitHub repository

ng-model problems in a directive

I am trying to write my first non-trivial directive and am having problems.
What I am trying to do is similar to http://plnkr.co/nYSBnm
This works as intended apart from the two inputs are using the same field. When I try and get them to use the intended fields, by putting
ng-model="record.{{field.name}}"
in the formInput attributes to replace ng-model="record.name" I get an error
Error: Syntax Error: Token '{' is an unexpected token at column 8 of the expression [record.{{field.name}}] starting at [{{field.name}}].
For what it is worth the plunk is at http://plnkr.co/O2uosO27khbcxZEeEb6V
I have looked at various somewhat similar questions on here and stack overflow and so far failed to get anything to work.
I would be grateful for any light anyone can throw on it.
Mark
I had also asked this question on the AngularJS group (I posted here after a day or so of silence on there). Then I got a response from Pawel Kozlowski who pointed out that
ng-model="record[field.name]"
"kind of starts to work" as he put it. There is a plunk here.
Not sure what he meant by his comment but when I put that in a real page and hit refresh I get two copies of the fields (unless I clear the cache), but he certainly solved the stated problem.

Resources