i'm working with angular js now for about ke 4 months and despite all the "first step failures" like not emphasising the async way anuglar thinks, I'm facing a problem I don't really understand. It's not that easy to describe.
I have a provider which registrates directives within the routeprovider's resolve function - during config phase. To compile programmatically preconfigured directives I create them on the specific controller call of each route. Acutally the directive I'm adressing here is a complex flotchart directive. It retrieves data from a rest api, transformes the retrieved data and prepares different kinds of option setups like proper stacked line charts or simple piecharts. Every single step takes its time, so I introduced promises to be sure that everthing is at it's right place before I finally call something like "$.plot".
So now I have the following situation: Imagine a singlepage app with two tabs. Each tab - like a first class menu item - refers to a new page with a new controller to process and new partials to render. For each page i have beside other directives one of these heavy flotchart directives to render. actually it takes about 5 seconds to render the chart. So we assume that we really start the app from beginning - like pressing F5. Now I enter the first page the first time and within the mentioned 5 seconds I switch tabs to enter the next page. I get to the next page, see different partials, layouts and stuff and a loading chart - but actually the directive of the first page is still bound to its link phase of that heavy flotchart directive (still preparing options for flot and calculating data to output graphically).
My problem is that this link phase actually really ends within a completely different template/route/controller context and gets stuck. It crashes with a console "replace" error from jquery.flot. I think this error means that flot tries to plot into a div which does not exist anymore. But that error occures just when I switch tabs during the link phase of the first page's heavy flot directive. It doesn't happen when the first page's chart is fully rendered and doesn't happen when the first page's directive hasn't entered its link phase (or am i missing something??). I tried placing some console.logs directly BEFORE hitting the jquery "$.plot" - remember only of the first page chart directive to dive into what's acutally happening. And the strange thing is when I manage to switch tabs within these magic 5 seconds, I still get the console log entries from the first page entry although I'm on a different page. And now guess what. That's strange - acutally two directive link phases are running side by side and one of them on a completely different view (or isn't it completely different, because its a singel page app?). Imagine I plot ("render") the chart in exactly the same div id - like $('#flot-chart'). so I have html parts containing id="flot-chart" on the first page AND on the second. when I now switch from the first to the second page (not finishing the first chart) I get the chart from the first page rendered in the #flot-chart div of the second page and like half a second later the actually correct chart rendered in that same div. So actually the link phase of the first page's chart directive ends in a completely different page in a way showing 2 charts consecutively. I know jquery.flot depends on DOM manipulation via jquery and that might be the problem (perhaps THAT'S the only real explanation for my problem), because jquery DOM manipulation is out from the angular way of life.
Or are there other explanations? I acutally solved the problem via $routeChangeStart listening and killing the $.plot process, but are there some hints, suggestions, explanations for that behaviour?
Plunker flot chart directive DOM collision
I have prepared a plunker which shows kind of a similar behaviour. i've delayed the creation of the directive and the directive's async data and option retrieval methods to somehow mock the behaviour of my app. this is non production code but describes simplified the way my problems occure. when you "fast click" the menu item one after the other many times, you can sometimes force angular to show 2 charts in one page. acutally one directive is linking and doing stuff in a different partial ? i know i'm missing something in my mind ... please give me a hint.
i used chrome for reproducing the error. stop the the plunker and press 'run'. directly after pressing 'run' click as fast as you can both links a couple of times.
Thanks a lot!
Related
I have an Angular/Cordova app and I'm trying to figure out how should I handle the HTML SELECT tag. What I would like to do is to open a new window with all the options in a list, the user picks one and returns with that value.
The problem is when I do that I lose all the data I had in the first screen as I am closing it when I move to the second one.
I am using Angular's UI.ROUTER. One thing, which I am not too convinced to do, is to save all data entered into StateParams, and when I return, place it back.
What would be the best approach?
It really depends on the use case. If you need to be able to "deep link" to the view where a link loads the view with the pop-up active then using ui-router and stateparams makes the most sense. If deep linking isn't a concern and the user must always select something then you can just use a service/factory/value/provider in order to share the data between the controllers during the lifetime of the app.
I am using UI Bootstrap for collapsing/expanding a div section based on user clicking a button similar to the example shown in the official site - UI Bootstrap-Collapse
I included the latest version of ui-bootstrap(0.14.2) but it was not showing the expected behavior. In particular the collapseDone() and expandDone() callbacks are never called after the $animateCSS is done the animations. (The animations are working properly, presumably because of the css transition property of the class 'collapsing' in twitter bootstrap.)
But when I inspected the UI Bootstrap site, I found that it's working properly as expected. (That's the class 'collapsing' is added during the animation and then removed after animation and then the class 'collapse' is added). So I checked the source code link for collapse collapse source code -github and found that finally is used for $animateCSS promise resolution rather than done in the ui.bootstrap.js code I included.
So could someone please explain the difference between finally and done methods of the $animateCSS function? The documentation [Angular Docs-$animateCSS][3] says that
there is also an additional method available on the runner called .done(callbackFn). The done method works the same as .finally(callbackFn), however, it does not trigger a digest to occur. Therefore, for performance reasons, it's always best to use runner.done(callback) instead of runner.then(), runner.catch() or runner.finally() unless you really need a digest to kick off afterwards.
So it looks like both should behave similarly. Could someone shed light on this, as there seems to be very little resources to learn about $animateCSS. (google search for $animateCss returns results for animate.css which seems to be part of the earler angular js verion (v1.2) - so that doesn't help)
I had forgotten to inject "ngAnimate" to my module. That way the animation by $animateCss was not successful, so the done() was never called. But finally will be called even if the animation is not successful.
I'm working on an app whose header's styling and UI options will change depending upon the state a user is currently in. For example, if a user clicks the Upload, Edit Profile, Create Playlist or Create Gallery buttons in the main menu, this will cause the following changes to the header:
the header's background to change color
the site logo will have CSS3 animation applied to it to make it slide into a new position
a specific message (depending on what state the user is in) in a word bubble will appear next to the logo
a Cancel button will manifest on the right edge of the header
This means there will be no static element on my site so logic tells me that I should place ui-view in my body tag thus making it look like this:
<body ng-app="app" ui-view>
I have never seen that used before anywhere so I've my doubts. Any ideas?
I'm not sure if this will or will not work technically, but I believe you should avoid it because:
It would require all your templates to include an outermost <body> tag which makes them less reusable
It's generally an unusual thing to do, and that comes with a cost, and I see no particular benefit to this, so IMHO the cost/benefit analysis says stick with what is conventional: use <div>s.
I suspect this may in fact work, but typically the tag with ng-app stays in place for the entire lifecycle of the app. There's a chance you may hit some weird errors if you swap out the tag containing ng-app. Thus many people put ng-app on the very first <html> tag and call it done.
But it's your app and the effort to try both approaches is very low, so just try them out and see (and let us know what you learn). I don't think you need to wait for stackoverflow answer from authority to test 2 slightly different HTML structures with your app.
No, please do not do that!
Having ui-view inside of the body tag may render many JS plugins/libraries unusable, especially in case if they dynamically add some HTML tags to document.body. In such cases, navigation to another view will reinitialize document.body and thus disregard previously added HTML tags.
E.g. this is the case with Firebase and Angular.js (AngularFire). I hope everybody who has come across the error
Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
will get to see this post. Just make sure you haven't added ui-view attribute to your body tag.
http://embed.plnkr.co/SPGNLd0bcmo2Xt2TAZcB/preview
Here we have a list of personnel information cards. If you click on one, the directive triggers a template to be loaded between that row and the row before it. My problem is that it only works once!
I believe that my problem lies somewhere in my compile statement, but I'm not sure:
$compile(controller.former)(scope);
What honestly baffles me is that even if you just click on the same card over and over again, ignoring all the others, it still just loads the one time. After the first successful load, the Template insertion is coming up empty. that is, isntead of the full template being inserted, i'm just getting:
<!-- ngInclude: 'focus.html' -->
And not the actual template located in that file. Does anybody have any Idea what is going on here?
I apologize in advance for the relatively complex directive, if anybody has any suggestions for refactoring it, I am an open book.
I think the problem I described is in async manner of getting the template from the server side. In case of cached content it didn't work. You used element bind to click which is anti-angular way and non-angular context (thus you needed yo call $apply). And in this case I guess the problem that $scope.apply works before the content of the compiled node itself is processed (because you get the template immediately from the cache). If you call $scope.apply from the $timeout function or (better) change the function to scope function linked with templates via ng-click it works as expected.
I think for the goal are are going to achieve you are taken a bit wrong design which looks a bit weird. Anyway I can show you the problem - you are using separate template which is compiled inside of the ng-include. ng-include directive actually performs $http request with using template cache (default behavior) because of this the data is loaded only once and the template isn't inserted. If you add random seed to the template URL or make $templateCache.remove('focus.html') before compiling that can solve your issue (but I don't think it's good solution as it makes http call each time). You should definitely re-factor your code to make it more clean.
I changed
controller.former = angular.element('<div ng-include="\'focus.html\'"></div>');
to:
controller.former = angular.element('<div><div ng-include="\'focus.html\'"></div></div>');
By wrapping the template into a parent container, I ensure that controller.former is always a reference to a container that holds all of the elements generated during compile time. without this fix, sometimes controller.former would only be selecting the comment node that precedes the inserted material. This resolves causes problems when i'm passing that selector into $animate.enter or $animate.leave, because it ends up only trying to animate that single comment node and ignoring it's next sibling.
Let us say I have 5 jsps in my web app and I import the ext-all.js in all 5 jsps. In all 5 jsps, I write an Ext.onReady(function(){});
When I hit the first jsp by typing it in the browser url, will all 5 onReady() functions be called? Or only the onReady in jsp 1?
I ask becuase I have the vague idea that onReady event is fired only once per Ext application. So it is different from onLoad() which fires just for the particular page.
Yes, all five functions passed to onReady() will be executed, but each only once. Ext.onReady acts as a latch which guarantees the input functions won't execute before the DOM is ready. If Ext.onReady() is called after "DOM Ready" event, the function will be executed immediately.
Just to make it clear: the "DOM Ready" event only fires once per page load.
I've assumed that by "JSP page" you mean "a page fragment", that is all five JSP pages are used to construct a single document and all the JS code is on the same page.
You have to remember that a JSP leaves the server as plain HTML and there is just one HTML document per page (let's forget about iframes, etc., for simplicity).