ng-repeat execution forces input to lose focus - angularjs

I have an input that is created using ng-repeat
<div data-ng-repeat="(index, answer) in currentQuestion['possible_answers']" class="form-group">
<label class="col-md-3 control-label">Answer {{ index + 1 }}</label>
<div class="col-md-8">
<div class="input-icon">
<i class="fa fa-sun-o"></i>
<input data-ng-model="currentQuestion['possible_answers'][index]" type="text" class="form-control" >
</div>
</div>
</div>
I want this to prepopulate the inputs with the values that are in currentQuestion['possible_answers'] and I also want any changes to bind to this variable as well.
However, everytime I start typing into one of these text fields, I type one letter and then it looses focus of the input box. I have a feeling that this is because I start typing and the data bidning updates currentQuestion. Because currentQuestion is updated, the ng-repeat is executed again.
Is there a way to make the ng-repeat action a one off action isntead of constantly revalutating?

Yes (looking at the symptoms, you did not show us the data) your issue could be because your model is the text in the array that you (may have), so whenever you update the model, it will trigger digest cycle since ng-repeat is tracked by the text. You can easily fix this by providing. track by $index, so that the ng-repeat is watched over and repeat watch gets updated only when the array changes in its length.
<div data-ng-repeat="answer in currentQuestion['possible_answers'] track by $index" class="form-group">
<label class="col-md-3 control-label">Answer {{ $index + 1 }}</label>
<div class="col-md-8">
<div class="input-icon">
<i class="fa fa-sun-o"></i>
<input data-ng-model="currentQuestion['possible_answers'][$index]" type="text" class="form-control" >
</div>
</div>
</div>
Demo
You can also use $index to get the array's index. you do not need to iterate with (key, value).
However i would just make my answer array an array of objects and get rid of all these issues, and it would just be (_note the usage of $index and ng-model):-
<div data-ng-repeat="answer in currentQuestion['possible_answers'] track by $index" class="form-group">
<label class="col-md-3 control-label">Answer {{ $index + 1 }}</label>
<div class="col-md-8">
<div class="input-icon">
<i class="fa fa-sun-o"></i>
<input data-ng-model="answer.text" type="text" class="form-control" >
</div>
</div>
</div>
Demo

The ng-repeat creates a new child scope for each item in the list. In this scope it knows index and answer. You bind the value of the input to something outside the scope, namely the same item in the array. Changing it triggers the list to be redrawn, which causes the input to loose focus.
<div data-ng-repeat="(index, answer) in currentQuestion['possible_answers']" class="form-group">
<label class="col-md-3 control-label">Answer {{ index + 1 }}</label>
<div class="col-md-8">
<div class="input-icon">
<i class="fa fa-sun-o"></i>
<input data-ng-model="answer" type="text" class="form-control" >
</div>
</div>
</div>

Related

AngularJs: Input value changes in display too using ng-model. I have tried ng-value and ng-bind too

<div class="form-group">
Name <input type="text" class="form-control" placeholder="item" ng-model="shoppingItem.itemName" required>
</div>
<div class="form-group">
Image URL <input type="url" class="form-control" placeholder="item" ng-model="shoppingItem.imgUrl" required>
</div>
<div class="form-group">
<button type="button" class="btn btn-success" ng-click="save()" >Success</button>
</div>
app.controller('recipebookController',function($scope,$routeParams,RecipeService,$location){
$scope.shoppingItems =RecipeService.shoppingItems;
$scope.rp="Route parameter value"+RecipeService.shoppingItems[0].itemName;
$scope.save = function(){
RecipeService.save($scope.shoppingItem);
$location.path("/");
}
});
<div class="col-xs-12">
<ul class="list-group">
<li ng-repeat="item in shoppingItems"class="list-group-item text-center clearfix">
<span style="font-weight:bold">{{item.itemName}}</span>
<img ng-src="{{ item.imgUrl }}" width="40" height="40"/>`
</li>
</ul>
</div>
When I enter save, the new data is saving row wise perfectly and displaying but when I re-enter new value into the input. The previously save value get changes as I type.
The issue that you're facing is that when you call RecipeService.save($scope.shoppingItem);, the RecipeService is saving the reference to your scope variable.
So, instead of assigning the variable directly, I might try doing something like this:
RecipeService.save(angular.copy($scope.shoppingItem));
This will create a new copy of the object referenced by $scope.shoppingItem and will allow you to edit one of those objects without affecting the other.

Nested Ng-repeat with ng-if passing proper $index

I have a nested ng-repeat with ng-if, inside each ng-if I have a check-box which is containing a ng-click function. I want to pass the nested ng-repeat's $index value through the function.
Here is my code.
<div class="student" ng-repeat="student in studentDetails.childNumber track by $index" ng-init=" studentnumber = $index">
<div ng-repeat="grmnt in student.garmentNumber track by $index" ng-init="grmntidex = $index" ng-if="grmnt.gurmentType === 'shirt'">
<div ng-click="garmentOpen = !garmentOpen;" class="z-depth-1 garmentelement">Garment</div>
<div class="garmentHeader" ng-if="garmentOpen">
<div class="garmentBody">
<div class="row">
<p class="col s6 center-align">
<input type="checkbox" id="BrandMeasurement" ng-click="msrmntSelector(grmnt)" />
<label for="BrandMeasurement">Brand Measurement</label>
</p>
<p class="col s4 center-align">
<input type="checkbox" id="BodyMeasurement" ng-click="msrmntSelector(grmnt)" />
<label for="BodyMeasurement">Body Measurement</label>
<div>
</div>
</div>
</div>
</div>
</div>
</div>
But the problem is when I am selecting the last grmnt element it's passing proper garment with id but after selecting the first element its not passing 2nd or 3rd or 4th any of the garment. In every case it's passing only the first element.
Can any one please tell me where I'm making any mistake (if any)??
Don't use ng-if on the ng-repeat just use a filter in the ng-repeat.
<div ng-repeat="grmnt in student.garmentNumber | filter:{gurmentType: 'shirt'} track by $index" ng-init="grmntidex = $index">
Edit: I think maybe the problem is in your labels. The label's for attribute needs to uniquely match the input's id attribute. Each row needs its own id/for pair. Use $index to do this.
<input type="checkbox" id="BrandMeasurement{{$index}}" ng-click="msrmntSelector(grmnt)" />
<label for="BrandMeasurement{{$index}}">Brand Measurement</label>

MDL not updating two ng-repeats' checkboxes values

I'm trying to display two ng-repeats showing the same array in two different formats: A simple list with checkboxes and the same list, with extended info and checkboxes.
The following example works fine in Angular.
<label ng-repeat="item in vm.items">
<input type="checkbox" ng-model="item.selected">
{{item.name}}
</label>
<div ng-repeat="item in vm.items">
<h1>{{item.name}}</h1>
<p>{{item.desc}}</p>
<input type="checkbox" ng-model="item.selected">
</div>
But if I use Material Design Lite, the checked state isn't updated, so when you click in one checkbox, the other one stills the same.
Real example:
<div ng-repeat="item in vm.items">
<div class="mdl-card__title" ng-style="{'background-image': 'url({{item.photo}})'}">
<h2 class="mdl-card__title-text">{{item.name}}</h2>
</div>
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-{{$id}}">
<input type="checkbox" ng-model="item.selected" ng-change="vm.updateMDL()" class="mdl-checkbox__input" id="checkbox-{{$id}}">
<span class="mdl-checkbox__label">Select {{item.name}}</span>
</label>
</div>
And in the list:
<div ng-repeat="item in vm.items">
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-list-{{$id}}">
<input type="checkbox" ng-model="item.selected" class="mdl-checkbox__input" id="checkbox-list-{{$id}}">
<span class="mdl-checkbox__label">{{item.name}}</span>
</label>
</div>
If I click one checkbox then the same one in the list, object's selected status gets toggled and the clicked checkbox doesn't change (since it didn't change to selected in the first place)
Anyone else facing this issue? Workarounds?

How can i add validation ng-maxlength into ng-repeat with error message?

I have a simple code :
<div class="row" ng-repeat="aDiagnosis in diagnosisListForPrescription">
<div class="col-md-4 padding-right-zero" id={{aDiagnosis.rowIndex}}>
<input class="form-control" name="aDiagnosisName" ng-model="aDiagnosis.Name" ng-disabled="true">
</div>
<div class="col-md-4 padding-right-zero form-group" show-errors id={{aDiagnosis.rowIndex}}>
<input class="form-control" name="aDiagnosisResult" ng-maxlength="200" ng-model="aDiagnosis.Result" />
<p class="help-block" ng-if="form.aDiagnosisResult.$error.maxlength">Too Large</p>
</div>
</div>
and use $scope.form.$valid to generate the error message.
But problem is since use ng-repeat every time it finds the same name and when i want to generate second list by clicking a button ,,first error message is gone and error-message now works on the second text (obviously).
So How can i generate error-message each and every time dynamically ,,so every text form in ng-repeat,it has it's own error-message.
You can generate dynamically name attribute of your inputs in ng-repeat. For example, you can put $index (or id of your objects or whatever you want) to generate unique name for your inputs.
<div class="row" ng-repeat="aDiagnosis in diagnosisListForPrescription">
<div class="col-md-4 padding-right-zero" id={{aDiagnosis.rowIndex}}>
<input class="form-control" name="aDiagnosisName-{{$index}}" ng-model="aDiagnosis.Name" ng-disabled="true">
</div>
<div class="col-md-4 padding-right-zero form-group" show-errors id={{aDiagnosis.rowIndex}}>
<input class="form-control" name="aDiagnosisResult-{{$index}}" ng-maxlength="200" ng-model="aDiagnosis.Result" />
<p class="help-block" ng-if="form['aDiagnosisResult-' + $index].$error.maxlength">Too Large</p>
</div>
</div>
Example on plunker.

Issue with ng-model and ng-repeat, duplicate forms

I have a page where multiple forms are created based on ng-repeat. Everything works fine until write something into the input and everything gets duplicated on all the other repeated forms input elements. I have used ng-model="Notify.message" which is nothing but object which takes the value from the input and sends to control on button submit and hence rest of the logic.
I am looking for when if one form is been filled, other forms should keep quite and shouldn't duplicate the values written in input text of form 1.
Here is the code:
<div data-ng-show="alluserposts.length > 0">
<div id="b{{userpost.id}}" data-ng-repeat="userpost in alluserposts" >
<div class="row" style="margin-left: -5px">
<form class="text-center" role="form" id=f1{{userpost.id}} name="userForm"
ng-submit="notify(userForm.$valid, userpost, apiMe)" novalidate>
<div class="row">
<div class="col-xs-8 col-md-4">
<div class="form-group">
<input data-container="body" data-toggle="popover" data-placement="top"
data-content="Any message which you would like to convey to post owner"
type="text" ng-model="Notify.message" data-ng-init="Notify.message=''"
id="u{{userpost.id}}"
placeholder="Enter a Message or Phone number" class="form-control"
required>
<p ng-show="userForm.name.$invalid && !userForm.name.$pristine" class="help-block">It is
required.</p>
<script>$(function () {
$("[data-toggle='popover']").popover();
});
</script>
<input type="hidden" ng-model="Notify.loggedInEmail"
ng-init="Notify.loggedInEmail = result.email"/>
<input type="hidden" ng-model="Notify.postId" ng-init="Notify.postId = userpost.id"/>
<input type="hidden" ng-model="Notify.destEmail"
ng-init="Notify.destEmail = userpost.userEmail"/>
</div>
</div>
<div ng-show="loginStatus.status == 'connected'" class="col-xs-4 col-md-2">
<button class="btn btn-primary" ng-disabled="userForm.$invalid || !userForm.$dirty"
type="submit">
Notify Post Owner
</button>
</div>
</div>
</form>
</p>
</div>
</div>
</div>
</div>
Issue fiddle - jsfiddle
Here you can when something is written in one input, other gets filled too :( . Also Notify is a Java mapped object and message is a variable inside it. Pls let me know how can this can be segragated!
You bind all of your inputs to same variable on $scope.
You must bind every text box to a distinct variable on $scope:
View:
<ul ng-repeat="post in posts">
<li>{{$index}}
<input type="text" ng-model="emails[$index]"/>
</li>
</ul>
Controller:
$scope.emails = [];
I am also at the starting phase of angularjs.
I have faced the same issue few days ago and resolved it by providing dynamic model name in ng-model like
<input type="text" ng-model="Notify[post.userEmail]" ng-init="Notify[post.userEmail] = post.userEmail" />
Working fiddle: Fiddle

Resources