AngularJS Watch Performance Issue - javascript

I need to code the following requirement. If it occurs that any form field changes, I need to hide a div with some content. My solution is using $watch with objectEquality == true for watching a complex object that bind to the form fields. But this complex object has around 100 fields to watch.
I think the solution described above addresses the requirement but I've read it could cause poor performance. So, is it the best solution? Do I have others alternatives ?

You may find that performance won't be an issue, 100 bindings isn't too bad. If you do need another solution though, you could put an ng-change listener on all the forms that want to watch, like so:
<input type="text" ng-change = "hideTheThing = true">
<div ng-hide = "hideTheThing"></div>

A lot of things that you think you need $scope.$watch for can and should probably be solved with an ng-change.
<input type="text" ng-change="formFieldChanged()">
Inside the function you can do whatver you like with other scope variables.

Related

VueJS - How to apply directives to all matching tags?

I'd like to apply a directive to all input tags programmatically. Two reasons for this are:
I don't want to have to go through all inputs in my app to add the directive
If I want to change the directive against all inputs at a later date, it's in one place.
Is this possible? I've reviewed the docs but they don't seem to mention applying it in any other way than applying the tag directly to the element.
My current code is like so:
<input type="text" class="form-control input-sm" id="price" v-model="model.doc.price" v-floating-label>
I was having a brain dead moment it seems. I just need an input component. I can then change what I need on there and it will update everywhere the input component has been used and instead of using the standard html input tag, I'll use my component.
Long day ...
I've answered this question myself instead of deleting it in case anybody else has the same brain dead moment in the future ;)
As per Evan You:
Vue.js compilation happens when you instantiate/mount the root instance.
See https://github.com/vuejs/Discussion/issues/77#issuecomment-60339440
I don't think what your are trying to do is sane: search and replace, coming out of the box in many text editors or IDE, will be really helpful for your two explained reasons.
You can bind that directive to a condition like so
<input type="text"
class="form-control input-sm"
id="price"
v-model="model.doc.price"
:v-floating-label=(condition)>
If condition == true , v-model-float directive will be applied to your input.
Update 1: From the comments, the implementation will still be the same, except you control the condition from one place. That way, you can remove the directive at a later date by simply setting that condition to false.

Angularjs validation with expression

I'm trying to implement input validation by angular expression ,I need to do that because i'm going to get validation data from database.
So I'm trying the following code
conttroller
vm.key="ng-required"
vm.value="true"
html
<input type="text" name="field" ng-model="name" {{vm.key}}="{{vm.value}}" >
but this make no change.
You can't use {{}} directive to create attribute dynamically(it will not work), and I don't think so that would be correct approach to do it. I'd like to suggest slight different way to deal with such validation, like you could take use of angular inbuilt directive like ng-minlength, ng-maxlength, ng-required, etc. which does take expression as their attribute values.
like for case it would be something like
ng-required="vm.value"

Clearing fieldGroup's fields when hidden

I need to clear all fields when they are hidden by a hideExpression, right now i have some code that adds a watcher to fields, and clearing them if they are hidden.
Problem is that this doesnt work for hideExpression's used on fields with fieldGroup's, since its apearently not allowed to add watcher to that type.
My example might explaine the issue better:
http://jsbin.com/fodijeziyu/1/edit?js,output
If you fill in the values, and click the hide checkbox, they should clear the model/view on the fields that gets hidden.
Generally on angular I would think different ways of doing things so that I won't be using watchers. It decreases performance a lot (and yes that sometimes might mean to use jQuery for it).
Now for angular-formly a way of doing what you want would be to use a function for hideExpression and achieve what you want.
Here is a working example.
Also read this link on the official angular-formly documentation.
There's an example on the website for this: http://angular-formly.com/#/example/very-advanced/remove-property-on-hide
You could use the watch with the true flag, in conjunction with your hideExpression:
$scope.$watch('someMiscForm', function() {
console.log('The model has changed!');
}, true);
Then alter/reset the fields you are interested in.

Modify ng-messages directive to only show when field is $dirty

I currently use markup like this for all my ng-messages
<div ng-messages="myForm.fieldName.$error && (myForm.firldName.$dirty || myForm.$submitted)">
<div ng-message="required">This field is required</div>
<div ng-message="minlength">Your field is too short</div>
</div>
It's a bit messy though and I'd like to somehow override or extend ng-messages so that it automatically checks to see whether the field is dirty or the form that the field is nested inside (by walking up the DOM maybe?) is $submitted.
I'd like this to be the default behaviour on all ng-messages in my site and can't forsee a situation when I'd need to show error messages when the input hadn't been used and the form hadn't been submitted so I think it's safe to override that behaviour, I just don't know how to do it.
I know I can replace the ng-messages entirely but then I'd have to recreate all of the default behaviours of that directive and these might change in future angular versions so I'd rather just extend it if possible. I don't know whether Angular provides any hooks for this (I've vaguely heard of decorator methods?) or whether to make a sibling directive, something like "ng-custom-messages" which just hides the element if the conditions aren't met?
So, I have a couple of ideas but I need a bit of a nudge to show how to implement them, just a bit of skeleton code if anybody is feeling charitable?
The answer to this was unfortunately to write a new form directive which automaticalley set all fields to dirty when submission happens.
Here is a short snippet of code...
angular.forEach( formCtrl , function ( formElement , fieldName ) {
if ( fieldName[0] === '$' ){
return;
}
formElement.$setDirty();
}, this);
formCtrl.$setDirty();
scope.$apply();
I recommend instead using an ngIf on the element that uses the ngMessages. Note that ngMessages accepts the $error property of NgModelController (or FormController).
<div ng-if="myForm.fieldName.$invalid && (myForm.fieldName.$dirty || myForm.$submitted)"
ng-messages="myForm.fieldName.$error">
<div ng-message="required">This field is required</div>
<div ng-message="minlength">Your field is too short</div>
</div>
As an aside, you may find it more appropriate to use $touched instead of $dirty in order to postpone error feedback until the element has lost focus (or the form has been submitted).

AngularJS - Setting value of radio button built using ng-repeat

Here's the HTML :
<div>
<ul>
<li ng-repeat="answer in answers">
<input type="radio" ng-model="$parent.selectedAnswer" name="answerText" value="{{answer.answerID}}"/>
<label>{{answer.answerText}}
</label>
</li>
</ul>
</div>
The need is to store the selected answer into LocalStorage and whenever the question shows up again, mark the radio button corresponding to that answer.
I'm able to store it in LocalStorage, retrieve it, but when I update the model, it doesn't select the radio button on the UI.
In my controller, I'm simply calling -
$scope.selectedAnswer = value.answerID;
where value points to the answer stored in the LocalStorage.
Please help.
EDIT:
A little more detail :
I'm managing the entire quiz page using a single route and controller. When one question is done, I fetch the next question and its corresponding answers. While populating the answers array in the controller ($scope.answers), I'm checking to match their IDs with the answer stored in the LocalStorage. Between each question, the answers array is emptied and populated again.
So you should be using ng-value to do this since it is an angular expression and not a text value. Using just value as you did worked in Plnkr but I know that sometimes it does not.
Also in general I have found that using the ng-controller="<ControllerName> as <ThisRef>" works a lot better in most cases. There are times when you still need the $scope variable (Like for form validation) but normally I have fewer scoping issues when using the controllers "this" variable over the $scope.
I made an example using the method I described:
http://plnkr.co/edit/XV3we6SDmmLmUGSwdVVT?p=preview
If your question has to do with HTML 5 Local Storage let me know, I wasn't sure
EDIT:
Alternatively you can try using $scope.$apply(); to accomplish this. If the scope is not in sync with the view then apply should fix that.
Here is another plnkr showing how I would generally do something like that. Note that the $apply is 100% not needed in that example but likely is needed in yours.
Here is my example:
$scope.updateModel = function(value) {
$timeout(function() {
$scope.$apply(function() {
$scope.selectedAnswer = value;
});
});
};
Notice I am using $timeout to make sure it doesn't happen while there is a digest in progress.
http://plnkr.co/edit/O7CBfTiWPrsbthFKlAOZ?p=preview
I hope this edit helps =)
Since you only have one controller, you don't need to specify $parent.
<div>
<ul>
<li ng-repeat="answer in answers">
<input type="radio" ng-model="selectedAnswer" name="answerText" value="{{answer.answerID}}"/>
<label>{{answer.answerText}}
</label>
</li>
</ul>
</div>

Categories