is using ng-model is inefficient? - javascript

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.

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>

Where to implement ng-click functions, controller or directive?

I'm a beginner in AngularJS, I understand most of the mechanics but I'm still grasping the "culture".
I'd like have clean separation between my HTML, DOM, data and communications.
My impression of a controller is a module that implements a "data" model, but is void of UI semantics (i.e. DOM manipulation).
However in my HTML, if I use an ng-click it is the controller's scope that is accessed for the click function implementation, which more then likely will need to call back into the DOM.
So where should I implement my click functions if I do not want DOM manipulation in my controller? Are DIRECTIVES the universal answer to this?
Suppose I have 2 controls on a page that need to interact with each other, should I create a directive on the parent of those controls parent that implements the click functions of both child controls? Or perhaps create a directive for each control and possibly pass the ID of the other control as an attribute? OR maybe a directive for the parent AND children?
--------- UPDATE 1 -----------
The following HTML is a simplified and contrived example that [hopefully] illustrates my question.
<div id="searchComponent">
<input id="txtSearchText" ng-keyup="..."/>
<input name="Go" id="btnDoSearch" ng-click="..."></input>
<div id="autoCompleteResults"></div>
<div id="fullResults"></div>
</div>
As the user types in the txtSearchText, the autoCompleteResults is populated, factoring in the usual minimum characters and timouts.
When the user presses or clicks the btnDoSearch, the autoCompleteResults is cleared/hidden and the fullResults is populated.
Finally, if the user begins typing new txtSearchText, the fullResults would be cleared/hidden and the autoCompleteResults is again seen with results.
Any guidance would be appreciated!
So where should I implement my click functions if I do not want DOM manipulation in my controller? Are DIRECTIVES the universal answer to this?
DOM manipulations, in my opinion, means something like document.querySelector(), addCliss, etc. ng-click is the event, which is supposed to deal with some business logic. Put it in the controller is fine.
Of course, directive is your another choice. directive is usually used to extract some reuseable components, such as modal, across different pages. If you repeat some code in different controllers, consider extracting them to directives or service.
Suppose I have 2 controls on a page that need to interact with each other,...
In short: use service, which is designed for that scenarios.
The general philosophy is to reference the DOM explicitly as little as possible. Most (if not all) things you want to do can be done by binding an aspect of you HTML element to a property on $scope, and manipulating that property. So you never have to do some like "Change the class of <span id="foo"> to red now that isRed is true". Instead you would have <span ng-class="{ red: isRed }>". So if you have two click handlers that share information, it's perfectly valid to have them change some common variable in your controller, and have state of the UI accordingly with DOM bindings. Directives are used more for reusing DOM elements, or when you do have to explicitly refer to DOM elements, i.e. adjusting the scroll properties of a div. You could use a directive to add the same click handler to many elements for example. Services can be used to share information, although if both of the controls belong to the same scope there's less of a reason to do that.

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 make angular faster when data binding it not required

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.

Using angular data binding and directives inside a non-bindable element

I have a form I'd like submitted as usual, so I stuck a non-bindable attribute on it. That works. But I have the need for a field to be filtered on input (specifically, converting a string to a clean version of itself on the fly, thus turning Some Input to some_input while it's still being typed) and this would be very easily achievable in Angular but since the entire parent element (the form) is non-bindable, Angular ignores all children.
I tried putting non-bindable on the submit button alone, but this produces no effect. How can I tell Angular to submit a form as usual, but to still allow angular directives and functionality inside said form, without resorting to vanilla JS and "onkeyup"?
On a more careful readthrough of the form API, I noticed this:
For this reason, Angular prevents the default action (form submission
to the server) unless the element has an action attribute
specified.
Specifying an action attribute was all it took. The form is now being submitted as usual, and I can use Angular within it.
I haven't used non-bindable attribute, but I don't think that it is possible with angular to have non-bindable parent element and use data-binding with children elements at the same time.
I would forget non-bindable and instead solve your scenario by a doing regular angular $http request on form click. You can configure it to look the same as regular form submit.
Or do you have any particular reason why you need browser to make your request from the form for you?

Categories