How to make angular faster when data binding it not required - javascript

Comparing angular with other libraries like handlebars for redering a list I notice about a 10x performance hit.
The table does not have to change when the model changes. If required I can redraw the table on button press or some other event.
Here is a plunkr with the comparison:
http://plnkr.co/edit/uwaNDVuPN4KvxRovdBfw?p=preview
1.) Am I timing angular correctly?
2.) Is there any way to speed up angular when binding is not required.

Check out this bind-once directives: https://github.com/Pasvaz/bindonce
It will bind you value only once to your view, and then delete the watcher. It will be much much faster.

Related

Buttons 'flickering' in angular application using ng-if or ng-show

I've had this same issue in two different angular applications I've worked on and yet I have been unable to find any discussion of this problem - which makes me think perhaps I am missing something. Let's say I have a view of a 'task' which can be in a number of different states including 'pending', 'accepted' and 'completed'. Different action buttons will be shown depending on the state of the task, for example:
<button ng-if="task.status === 'pending'" ng-click="ctrl.acceptTask()">Accept</button>
<button ng-if="task.status !== 'accepted'" ng-click="ctrl.acceptTask()">Flag</button>
<button ng-if="task.status === 'accepted'" ng-click="ctrl.flagTask()">Complete</button>
The issue is that when the user clicks the accept button, for a brief period both of the buttons below will be displayed. It's as if angular is sequentially working through the DOM and for the brief period between ng-ifs, both the 'flag' and 'complete' button are displayed because only one has been updated. This occurs for ng-show as well.
Note, this is not an issue that can be solved with ng-cloak, which is only there to prevent a template being displayed before angular has done its magic.
Given I've encountered this issue on both of the two large angular applications I've worked on, it must be a common problem. Any suggestions as to how this is typically solved? (PS, the above HTML is just an example of what I mean, it's not my actual template.)
That is because ngIf completely removes and recreates the element in the DOM. When an element is removed using ngIf its scope is destroyed and a new scope is created when the element is restored. The scope created within ngIf inherits from its parent scope using prototypal inheritance. also,ngIf recreates elements using their compiled state.
Indeed having ngIf evaluate to false will remove the element. But only if it is not set to true immediately after, as angular only refresh the DOM (your HTML) when it has the opportunity to do so, i.e. after the current synchronous block of code.
Below code snippet does not have any effect:
$scope.task.status= 'pending'; <br/>
$scope.task.status= 'accept';
This will
$scope.task.status= 'pending';
// $timeout will wait for a digest cycle before executing the code
// Angular will remove the button from the DOM
$timeout(function() {
$scope.task.status= 'accept';
// Here a second digest cycle happen, angular will put the button back into the DOM
});
Please note that it might be due to your angular app emerges many watchers due to heavy DOM contents. I have made one simple plunker to replicate your scenario but not facing it. You can check and reduce watchers by adding available chrome plugin and/or many others way like this. Just google it and you can find a treasure of lots of fruitful information to improve watcher count of your app.
Ok now I got it what exactly you do require here:) See !! This is what angular docs told us "Note especially the powerful ng-switch that should be used instead of several mutually exclusive ng-shows." One of the good read on "When to use what!!!". So i hope flickering buttons will not happen if you use the ng-switch at those instances or alternatively we can achieve the scaffold of solution that is quite generic and works without worrying about current scope at all by using directive initialization function to inter-instances communication. See this in which i have applied class to multiple buttons in mutually exclusive way.
The below ng-if statements ensures that it will display one at a time. If you see it both at the same time it should be because something in your code is messing with angular digest cycle which delaying the two way binding of the fields and rendering of the buttons.
<button ng-if="task.status !== 'accepted'" ng-click="ctrl.acceptTask()">Flag</button>
<button ng-if="task.status === 'accepted'" ng-click="ctrl.flagTask()">Complete</button>

How to trigger Angular to recompile a template from JQuery?

I understand that the solution will be non-ideal -- I'm working with legacy code and have many constraints.
On a page in my app, the user choose between one of several forms they want to fill out. When selected, we use JQuery to load the selected form into the DOM. In this newly loaded form, we need to use an angular directive, but angular doesn't know that anything has changed (since JQuery handled the state change), so it doesn't recompile the markup that contains our directive.
How can I let angular know that it needs to make another pass through the DOM?
$scope.apply() will trigger a new check of the DOM.
If you use it with no check, it may fire an error inside angular.
You can use it, wrapped into a $timeout(), so that it will be triggered after a current digest (if there was)
$timeout(function(){
$scope.$apply();
});

How to disable Angularjs completely after the page is fully loaded

I want to disable all the functionalities provided by Angularjs, but only after the pages and all components have been loaded fully.For example "ng-click" should not work any more.
I tried to set the "ng-click" attr to null but, it still works when clicked.
Thank you
You can destroy angular app $scope that means it will disable only two way binding of scope variables using $scope.$destroy() method nothing more than that(If you want to disable two way binding on start up load then you need call $destroy() in $timeout).
But the event listener won't get disabled from angular app which are register while angular app is initialized on page. You can only achieve this by maintaining any flag (this is hacky way).
Here is Fiddle which demonstrate what i want to say.
Thanks.

is using ng-model is inefficient?

I have a very simple use case in my application. when user double click on a record it will show an input box and after submitting new text and hit enter. that will update the record.
all I want to do is grab input elements value and fire service.
<input type="text" ng-model="updatedListTitle" ng-enter="updateList(list.id, updatedListTitle)">
as you can see I am using ngmodel to grab data in my controller. the problem is with every keystroke it is firing all my watchers unnecessarily (even though i know angular is fast and you can fire like 1000 watcher and all ). but just grabbing the element using jquery would be more efficient.
is there any better approach to handle these situation where i really don't care about 2 way binding
Well..., in my opinion the way you have done it is perfectly idiomatic angularjs and I wouldn't start to micro-optimize it.
(Remember "Premature optimization is the root of all evil" as well as Michael A. Jacksons rules about optimization and other quotes - http://en.wikipedia.org/wiki/Program_optimization )
No, I don't think there is any sensible way to do it without jQuery (or at least jqLite or manual dom access).
If you do that, you should encapsulate it in a directive (which arguably is the only place explicit DOM access/manipulation should be).
Can you use ng-blur here instead of ng-enter and then check to see if the model is dirty and then do the service call?
AngularJS sets the CSS classes ng-pristine and ng-dirty on any input field you've used ng-model on, and your controller has the properties $pristine and $dirty which you can check to see if the form is dirty or not.
That might be a bit more optimized. I would still do this in a directive though.

What's the angular way to handle redraws?

I'm making a charting application that allows you to create graphs using a drag and drop interface.
I have highcharts and I'm using the highcharts-ng directive.
This directive watches the title, options, and series. And when a person makes a change, I process them and make changes to the options object. Then highcharts-ng redraws the chart.
The problem I'm finding is that I change a few properties in a row such as options.xAxis and options.yAxis, and whenever I do this the application is lagging a bit because it's launching a redraw for every change.
So what would be the angular way to approach this, while still being efficient?
A potential solution I thought of was to add a flag to the highcharts-ng directive, and have it trigger whenever it's changed. And then just change it after I'm done processing the data. Another potential solution is to listen for a certain event inside the highchart-ng directive, and then trigger the redraw whenever that event is received. But these solutions seem/feel a bit hacky to me.
Angular does its own dirty checking and (ideally always, not but really) rewrites the Angular-controlled sections of the DOM whenever their corresponding view models change. I think that this behaviour is so fundamental to Angular that if you don't like it, you'd either better work around it, or use a different databinding framework.
The workaround I'd recommend is basically what you described in your first option: a view model inside the view model. Have a private variable inside the directive's scope which tracks the changes you're interested in, the ones which happen more frequently than you want to redraw. Then when you're ready to redraw (you'll need your own logic for what determines "ready"...time? a particular kind of change? a particular threshold of change?), update the real view model by setting your private variable back to its original field on the real view model.
Code sketch:
// (inside the directive)
var _options = $scope.options;
// ...
// rapidfire updates happen; save them to _options rather than $scope.options
// ...
// now you're ready to redraw:
$scope.options = _options; // angular now knows $scope is dirty and triggers the redraw

Categories