How to process a template and remove all bindings from it? - angularjs

I need to create a page with AngularJS that will display thousands of log entries. These entries will be created with the help of Angular templates, but, since they are unchangeable once printed, I would like to have the bindings removed from the resulting entries.
Is this possible? I looked at $compile (keeps the bindings) and $interpolate (doesn't do directives if I understood correctly, only does {{ }} ) and haven't found an answer to my question.
How would you approach such a problem?

You can use the directives defined in the Bind Once library in order to perform binding for immutable data.

I didn't find an answer to my problem, the problem though can be circumvented with an "infinite scroller" such as the one described here: https://medium.com/p/463bc649c7bd that "disappears" log entries that aren't in the viewable window.

Related

angularjs ng-repeat adding html comments on dom

Why does angularjs cause multiple comments in between render data? Inspecting DOM childNodes causes extra nodes which takes up memory. Is there a way to remove them?
Angular needs these comments to operate with some directives. Removing those comments breaks Angular and it is not allowed to do so currently.
It is also explained in this Github issue.
Angular uses the comments do figure out where the last element was placed in relation. It's here in the source code https://github.com/angular/angular.js/commit/9efa46ae640cde17487c341daa9a75c0bd79da02
The comments are written client-side so there's no impact on transfer, and if your application is that memory conservative, Angular is probably the wrong language to use in the first place.
I find these comments annoying too. You can turn off the content of them though. Just set debugInfoEnabled to false in the config:
$compileProvider.debugInfoEnabled(false);

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.. :)

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

How to speed up an AngularJS Application?

I have an AngularJS app with a paged grid (two nested ng-repeat). One page has approximately 25x40 input elements. In the beginning that made 1000 bindings, the paging performance was acceptable.
But then the complexity of page grow: dynamic classes, varying context menues, conditional content for each cell of the grid. And with estimated 6000 bindings (6 per input element) the paging got unusable slow.
My question is: how do I generally approach performance problems in AngularJS?
The obvious first step ist to measure. But the results of the Chrome Profiler do not tell me that much, far from knowing how to proceed.
Self Total Function
-----------------------------------------------------------------
24 ms 2.79 s angular.js:7997 Scope.$digest
1 ms 1 ms controllers.js:365 setViewportData
16 ms 692 ms angular.js:13968 ngRepeatWatch
8 ms 22 ms angular.js:6439 extend.literal
9 ms 1.22 s angular.js:14268 ngSwitchWatchAction
16 ms 45 ms angular.js:12436 ngModelWatch
0 621 ms angular-ui-4.0.js:264 initDateWidget
0 13 ms angular.js:12859 ngClassWatchAction
0 70 ms angular.js:14184 ngStyleWatchAction
1 ms 5 ms angular-ui-4.0.js:261 getOptions
0 16 ms angular.js:579 copy
0 1 ms angular.js:4558 interpolateFnWatchAction
1 ms 2 ms angular.js:5981 token.fn.extend.assign
0 37 ms angular.js:8151 Scope.$eval
1 ms 1 ms angular.js:6137 extend.constant
14 ms 16 ms angular.js:651 equals
1 ms 1 ms angular.js:4939 $interpolate.fn
Aside: is there any chance that 'Object.observe()' will speed up things in the future (ignoring 'initDateWidget', that's obviously a different topic)?
The thing you can do that will speed up your Angular app the most is to reduce those bindings where you can. One way to do this would be to create a directive that built out the table for you with DOM manipulation rather than using ng-repeats. This will reduce the number of overall watches you have to process, and make that $digest a lot faster.
I know it's ugly to do that, but Angular's not really meant to set up 3000+ bindings. Since it does a digest and it's not an observer pattern, it really slows things down have that many set up.
You could even do a hybrid approach, where you still used the ng-repeat, but all of the values were placed in the DOM with straight DOM manipulation from a custom directive, thus avoiding all of the bindings.
If you have not done so, please install the AngularJS Chrome plugin, Batarang, which will help you pinpoint which of your bindings are causing you grief. https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en
As the other answer suggests, what you're looking for is likely a small case of an infinite-scroll setup for your table where the model you bind to is the subset you're displaying onscreen.
The ng-grid component implements this and might be worth looking at to either use it directly or steal the technique. http://angular-ui.github.com/ng-grid/
Resource
This post about angularJS performance on large lists has a nice overview of the options you have for performance tuning.
Above answers (except for the Batarang plugin) are also mentioned within. This is just an overview of the tips in that article.
Reduce data with limitTo (pagination)
One of the more obvious solutions is to reduce the amount of bindings by reducing the number of items in your view. Pagination of data can be done with the limitTo filter on ng-repeat.
Example at How to improve performance of ngRepeat over a huge dataset (angular.js)? That article also has a jsbin example linked.
Also make sure not to use an inline method for data providing since that will be evaluated on every $digest.
<li ng-repeat="item in filteredItems()"> // Bad idea, since very often evaluated.
<li ng-repeat="item in items"> // Way to go!
Remove bindings with bindonce
Another obvious solution is to remove bindings on specific elements. Sure this means that updates won't be reflected in the view anymore.
The bindonce solution does a lot more than just removing the 2 way binding. Basically it waits for the value to be bound once before the binding is removed. Best read for yourself. Check the bindonce project for details.
In the article listed on top there is also information about a pattern working with 2 lists. One for visualisation and one as a data source.
Use ng-grid
Ng-grid has the advantage that it only renders the elements that are currently visible. Read more at http://angular-ui.github.io/ng-grid/.
Similar ng-if removes the hidden elements from the DOM tree completely while ng-show only keeps them in place but hidden. Take in account that ng-if will put a copy of the original (original is key, not the changes that is) element in place when shown again.
Tips for filtering
The article also has some great tips for filtering lists.
Like using ng-show to hide the filtered out elements since this way no sublist has to be created of the data.
And Another technique referred to as "debounce user input". That last option is to wait with the filtering until user has stopped typing. Including a jsfiddle example.
More
More tips can be found in the linked article. There are resources listed there too so that should be a good starting point. The most obvious onces and quick wins are listed here I believe.
Another nice writeup is How does data binding work in AngularJS?
A bit late but maybe this works for you:
https://github.com/Pasvaz/bindonce
You can use it on those binding that are not meant to change so $digest won't process them anymore.
In angular 1.3 and more you can bind once by using :: no need to use other 3 party js
<li ng-repeat="item in :: items">
This is good if the items will not change so you can bind them once
I have encountered performance issues , when the amount of listeners exceded 1000+ in a data grid component.
I solved this issues , using a directive which builds my view using react.js .
the directive exposed an update function .
each time the data changed (in the controller), the update function triggered the directive , and then the react.js engine did the rendering efficiently.
i know its a big overhead to use a second major framework inside of an angular project, and this is not real data binding magic. but its working much faster.
eventually i stopped using angular.js and moved to react.js + FLUX . i think its better but i know its not easy to shift from angular, but its worth it.
Angular directive that uses react.js
Limiting the number of watches can often go a long way. Here is a summary of techniques that are effective for reducing the number of watches
http://www.syntaxsuccess.com/viewarticle/547a8ba2c26c307c614c715e
I had performance issues with ng-grid with large data, it was solved by replacing it with Angular Grid. The demo on it's website shows it managing 100,000 rows easily.
I have wrestled with this for a few weeks. I have found two things have made a substantial difference:
(i) ONE TIME BINDINGS: Use one-time bindings where you can; and
(ii) DEBOUNCE: For input that does not need to me immediately propagated, but can wait 250ms, set a debounce setting. This has made an INCREDIBLE difference to my large ng-repeat table. I cannot emphasise how effective a debounce setting has been. (see here: https://docs.angularjs.org/api/ng/directive/ngModelOptions)
bject.observe() is a proposed mechanism for bringing true data-binding to the browser. It exposes a mechanism for observing changes to objects and arrays, notifying others of mutations made to these objects.
<!DOCTYPE html>
<html>
<head>
<base target="_blank">
<title>Object.observe()</title>
<link rel="stylesheet" href="../css/main.css" />
</head>
<body>
<div id="container">
<h1>code_lab_by_shail Object.observe()</h1>
<p>An object <code>o</code> is created and <code>Object.observe()</code> is called on it.</p>
<p>Three changes are made to <code>o</code> and <code>Object.observe()</code> records these changes as shown below.</p>
<p>Use the console to find out what happens if you make further changes to <code>o</code>: it's defined in global scope.</p>
<p>Call <code>Object.unobserve(o, observer)</code> to stop observing changes.</p>
<p id="data" style="font-size: 14px;"></p>
<script src="js/main.js"></script>
View source on GitHub
</div>
<script src="../js/lib/ga.js"></script>
</body>
</html>
You can also improve performance in general by Disabling Debug Data

How do I create a new, uh, thing in Angularjs?

I think the vagueness of the question is part of the problem, so my real first question is, what do you call, in Angular, the thing.
The thing I'm trying to name is the view plus the controller, over the model of a single object. I don't even know what to call it. For things I know ahead of time I'm going to need, I've been creating directives, but what do you call one instance of the thing that a directive creates?
I have several situations where all of a sudden (in response to some external event), I have a new object in the model and I want to show it on the screen. Angular seems to want me to list all the possible views ab initio in their parent view, but that isn't really reasonable in my case. How, for example, would I list all the pop-ups and tool-tips and other stuff.
I'm down in some little edge case, deep in the controller code, and it needs to add something to the current view. What's the accepted practice.
Incidentally, the $route/ng-view is one case of exactly this. The view containing the ng-view, and the ng-view DIV itself, have no idea what the $route module is going to put in the ng-view. I need the more general case of this strategy.
EDIT
People keep asking for an example. How about this: I'm making an equipment-requisition app. When a user asks that one of the 1000 different type of equipment be sent to him, I need to display a pop-up that gathers addition information specific to that type. If he asks for a screwdriver, the pop-up will ask about blade size, neck length, and handle composition; if he asks for an airplane, it will be a wizard ask him about engine size, fuel tanks, seating arrangement. All the app knows on start-up is the list of all equipment types, and the name of the UI element that gathers all subsequent information about each particular type.
I'm down in some little edge case, deep in the controller code, and it needs to add something to the current view. What's the accepted practice.
Somewhere, you need to define all of the views you'll need -- e.g., all of the equipment popups. You could put each view into a separate file and use ng-include to dynamically pull in the one you currently need to display. Define a property on your $scope (e.g., $scope.equipmentTypeViewUrl), then
<div ng-include src="equipmentTypeViewUrl"></div>
Since ng-view can only appear once per page, ng-include is probably not what you need to use if you need multiple levels of routing.
See also
create a single html view for multiple partial views in angularjs
https://groups.google.com/forum/#!topic/angular/xIIyGpW8KUk/discussion
App design using Angular js
Client Side Template with view per role
I think the problem is that you think that you need to create the "thing" in controller, but actually you don't. The way two-way data binding works is that you change some attribute value, and the view changes based on that. I've never seen a use case where that's not enough, pop-ups and tooltips notwithstanding.
However, if you really must have the controller show something, you could utilize angular's events to do that. You would need two parts: a directive responsible for showing stuff (modifying DOM), and the controller. The controller would $broadcast an event with some parameters, and the directive would listen to those events using $on and react accordingly.
I'd just make sure I had some useful code coming in as the model...
<div class="row" ng-repeat="attribute in attributes">
<div class="widget" ng-repeat="input in attribute.inputs">
<input type="{{input.type}}" ng-model="input.value" />
</div>
</div>
I'm extremely limited in my knowledge, but all I know is if you have a definite structure to your model you can build a view that reacts to it dynamically.
If all of those things are related to your original object (properties or in some way other) you could loop through the data, display the properties and if need use the keys and filters for a label. Imho it's not really an angular question, more one if your data structure. If you have a good data structure you could use a service for creating a related data-object.
For a related popup, you can use a directive and even process the model data there (only recommended if it has a consistent structure).
If you dislike this approach, you can process the data directly in the template.
But without more specific details, there will be no definite answer.

Resources