ngModel is reflecting to all textareas - javascript

I have multiple textareas (looping with ngFor and adding new divs with textareas inside). What i need is for every textarea to have separate ngModel and i don't want to directly bind this to property from object in dataArray - for example:
[(ngModel)]='data.note' or [(ngModel)]='data.feedback' .
This works but I don't have feedback property in dataArray so it won't for work for second textarea.
For example with my current implementation change in one textarea is reflecting in all other textareas. I tried with index approach but getting error:
ERROR TypeError: Cannot read property '1' of undefined
<div *ngFor="let data of dataArray; let index=index;trackBy:trackByIndex;">
<div class="card-body">
<form class="form">
<div class="form-body">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<textarea name="note" [(ngModel)]='selectedNote' class="form-control"
rows="2"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="form-group">
<textarea name="feedback" [(ngModel)]='selectedFeedback' class="form-control" rows="4"></textarea>
</div>
</div>
</div>
</div>
</form>
</div>
With current code if i add some text in first textarea with name 'note' that change is reflected for all textareas with name 'note'. As mentioned tried with adding
[(ngModel)]='selectedFeedback[index]' but i am getting error.
Also tried with giving different names to textareas:
<textarea name="note{{index}}" [(ngModel)]='dataArray[index]' rows="2"></textarea> OR
<textarea name="note{{index}}" [(ngModel)]='selectedNote' rows="2"></textarea>
but change is reflecting for each textarea again.

You can try it with any array, I am using data(n) function to return an Array of length n. In this example it's just for iteration
<div *ngFor="let item of data(8); let i = index">
<textarea [(ngModel)]='values[i]'></textarea>
</div>
// To reflect changes
<div *ngFor="let item of data(8); let i = index">
<div>{{ values[i] }}</div>
</div>
With TS
export class AppComponent {
values = [];
data(n) {
return Array(n);
}
}
Working example in Stackblitz.com

ngModel binds with the name property. So if you want to use multiple textarea try using different name attribute. You can iterate over like -
<ng-container *ngIf="let data of dataArray; index as i">
<textarea name="feedback_{{i}}" [(ngModel)]='selectedFeedback' class="form-control" rows="4"></textarea>
</ng-container>

Related

What is the easiest way to make editForm in Angular?

In my database i have many users which has many recipes.
Every recipe has some properties and collection of ingredients.
Below is screenshot
Recipe with all properties
So when user display recipe to edit on page should appear (form) recipe with loaded current data. This is kind of working because i can see the data but i think it's no done good.
I have form which is working fine without array (ingredients). Could you tell me how i should add ingredients to my edit form?
I'd be grateful if you see at my code and give me feedback and hints what i should change.
export class RecipeEditComponent implements OnInit {
#ViewChild('editForm') editForm: NgForm;
recipe: IRecipe;
photos: IPhoto[] = [];
ingredients: IIngredient[] = [];
uploader: FileUploader;
hasBaseDropZoneOver = false;
baseUrl = environment.apiUrl;
currentMain: IPhoto;
constructor(private route: ActivatedRoute, private recipeService: RecipeService,
private toastr: ToastrService) { }
ngOnInit(): void {
this.loadRecipe();
}
loadRecipe() {
this.recipeService.getRecipe(this.route.snapshot.params.id).subscribe(recipe => {
this.recipe = recipe;
this.initializeUploader();
})
}
updateRecipe(id: number) {
this.recipeService.editRecipe(id, this.recipe).subscribe(next => {
this.toastr.success('Recipe updated successfully');
this.editForm.reset(this.recipe);
}, error => {
this.toastr.error(error);
});
}
}
HTML
<div class="container mt-4 border" *ngIf="recipe">
<form #editForm="ngForm" id="editForm" (ngSubmit)="updateRecipe(recipe.id)" >
<h5 class=" text-center mt-2">Recipe details:</h5>
<div class="form-group mt-3">
<label for="city">Name</label>
<input class="form-control" type="text" name="name" [(ngModel)]="recipe.name">
</div>
<div class="form-group">
<app-ingredient-editor [ingredients] = "recipe.ingredients"></app-ingredient-editor>
<div *ngFor="let ingredient of recipe.ingredients; let i = index">
<input class="form-control" type="text" name="{{ingredient.name}}" [(ngModel)]="ingredient.name">
<input class="form-control" type="text" name="{{ingredient.amount}}" [(ngModel)]="ingredient.amount">
</div>
</div>
<div class="form-group">
<br>
<p>Add recipes</p>
</div>
<h5 class=" text-center mt-4">Description</h5>
<angular-editor cols=100% rows="6" [placeholder]="'Your description'" [(ngModel)]="recipe.description" name="description"></angular-editor>
</form>
<button [disabled]="!editForm.dirty" form="editForm" class="btn btn-success btn-block mb-5 mt-5">Save changes</button>
</div>
For now it's look like:
Form on page
When i delete ingredient name while changing on the console i have following error:
recipe-edit.component.html:12 ERROR Error: If ngModel is used within a form tag, either the name attribute must be set or the form
control must be defined as 'standalone' in ngModelOptions.
Problem is that part of code:
<div *ngFor="let ingredient of recipe.ingredients; let i = index">
<input class="form-control" type="text" name="{{ingredient.name}}" [(ngModel)]="ingredient.name">
<input class="form-control" type="text" name="{{ingredient.amount}}" [(ngModel)]="ingredient.amount">
</div>
</div>
But i don't know how to make it working..
How to add add array to template-driven form?
In my case i need to display current ingredients and be able to edit them.
I have tried something like this :
<input class="form-control" type="text" name="ingredient[i].name" [(ngModel)]="ingredient[i].name">
<input class="form-control" type="text" name="ingredient[i].amount" [(ngModel)]="ingredient[i].amount">
But id doesn't work
The problem is that the property name on the form must be defined in order for angular to know which input to update. You're binding name to the same property that the editable model is set to which means the user can edit it and in fact delete it, which isn't good.
The solution is to change it to a unique value that doesn't change. This should work:
<div *ngFor="let ingredient of recipe.ingredients; let i = index">
<input class="form-control" type="text" name="name{{ingredient.id}}" [(ngModel)]="ingredient.name">
<input class="form-control" type="text" name="amount{{ingredient.id}}" [(ngModel)]="ingredient.amount">
</div>
</div>
Link to stackblitz showing it working: https://stackblitz.com/edit/angular-10-base-template-q243lw?file=src%2Fapp%2Fapp.component.html
Edit: fixed bug in original post and added link to stackblitz

How to restrict ngModel changes effect to other inside ng-template in angular7?

This is my code
<ng-template #rowDetailsTmpl let-row="row">
<div class="row" style="padding: 10px 30px;">
<div class="col-sm-5 form-group">
<label> Add Operator </label>
<input type="string" id={{row.DeskId}} name={{row.DeskId}} (ngModelChange)="onChangeOperator($event)" class="form-control"
placeholder="Search Operator" [(ngModel)]="selectedOperatorEmail">
</div>
#ViewChild('rowDetailsTmpl', { static: true }) rowDetailsTmpl: TemplateRef<any>;
this._dataTableService.rowDetailsTemplate = this.rowDetailsTmpl;
In my code input text field using inside ng-template , i set id and name dynamically , but when i change value in textbox it automatically reflect to other input fields. so how to solve this problem in angular7.
in component define the model like array:
selectedOperatorEmail: Array<any> = [];
in html define ngModel define like this:
[(ngModel)]="selectedOperatorEmail[row.DeskId]"

Using ng-model on a dictionary value within an array

So I have an array that I've populated like so:
tempArray[tempProductName] = null;
tempArray[tempAttributeName] = response.data[0].Attributes[k].Name;
tempArray[tempAttributeValue] = null
$scope.newDayDisplay.push(tempArray);
I'm using this information in an ng-repeat like so:
</div>
<div class="row" ng-repeat="result in newDayDisplay">
<div class="col s2">
<md-input-container><label>{{result.AttributeName}}</label><input type="number" id="new-day-ARP-input" ng-model="result.AttributeValue" /></md-input-container>
</div>
<div class="col s4">
<label>Select Material</label>
<md-select ng-model="result.ProductName" md-on-close="clearSearchTerm()" data-md-container-class="selectdemoSelectHeader">
<md-select-header class="demo-select-header">
<input ng-model="searchTerm" type="search" placeholder="Search For Material..." class="demo-header-searchbox md-text">
</md-select-header>
<md-option ng-value="{{mat.ID}}" ng-repeat="mat in Materials | filter:searchTerm">{{mat.Name}}</md-option>
</md-select>
</div>
<div class="col s2">
<p>{{result.AttributeValue}}</p>
<p>{{result.ProductName}}</p>
</div>
</div>
The issue is that whenever I try to send this information as a post (the array) it does not bind the values for result.AttributeValue or result.ProductName to the respective value in the array "newDayDisplay".
So for example, I get
But I want to be able to change those null values to the values entered on the modal.
I imagine this has to do with ng-repeat not working the way that I think that it does - does anyone know how to fix this?
Note that even if I do:
<div class="col s2">
<p>{{result.AttributeValue}}</p>
<p>{{result.ProductName}}</p>
</div>
I'm able to see the values I've selected, but the post doesn't take the values with it.
Here's an example of that binding:

Angular: Get ng-model by ng-attr-id?

Is there a way to get ng-model value by ng-attr-id ?
I'm making a Comment/Reply box and I would like to get the value of the current reply.
Here is my html--
<div class="comments-list" ng-show="CommentsLoaded">
<ul>
<li ng-repeat="comment in comments">
<div>
{{comment.content}}
</div>
<div ng-click="showReply(comment.id)">
Reply
</div>
<div ng-class="hideReply" ng-attr-id="{{'reply-'+comment.id}}">
<textarea ng-model="replytxt" ng-attr-id="{{'replytxt-'comment.id}}"></textarea>
<div class="form-group">
<button type="button" ng-click="sendReply(comment.id)">
Publier
</button>
</div>
</div>
</li>
</ul>
</div>
Here is the angularjs--
$scope.sendReply = function(commentId){
var elm = document.getElementById('replytxt-'+commentId);
console.log(elm);
}
The above function show this in console:
<textarea ng-model="replytxt" ng-attr-id="{{'replytxt-'+comment.id}}" class="ng-pristine ng-valid ng-touched" id="replytxt-31"></textarea>
You don't need to retrieve element value by its selector. Do pass replytxt value inside sendReply function itself on click.
ng-click="sendReply(comment.id, replytxt)"
$scope.sendReply = function(commentId, replytxt){
Suggestion: rather than having replytxt there independently as ng-model, you can put it on comment level property like comment.replytxt, so that you don't need to take care of passing replytxt value separately to server.
ng-click="sendReply(comment)"
Code
$scope.sendReply = function(comment){
console.log(comment.id, comment.replytxt);
}

Validate a form field with a dynamically given name in angular

I create a form dynamically in the view by iterating through an object that has the different questions to be asked to the user. One of the attributes of every question is formFieldName which is a a random string I use to give each form field a different name.
<form name="includedForm.newRequestForm" class="form-horizontal" role="form" novalidate>
<div ng-if="message.question.attributes.structure.type == 'object'">
<div ng-repeat="(index,objField) in message.question.attributes.structure.properties">
<div ng-if="objField.type == 'array'" class="form-group" show-errors>
<label for="{{objField.formFieldName}}" class="control-label col-sm-6">{{objField.title}}
<br /><i><small>{{objField.description}}</small></i></label>
<div class="col-sm-6">
<select class="form-control" name="{{objField.formFieldName}}" multiple ng-model="objField.userValue" ng-required="objField.required">
<option ng-repeat="option in objField.items.enum" value="{{option}}">{{option}}</option>
</select>
</div>
</div>
<div ng-if="objField.type == 'boolean'" class="form-group" show-errors>
<label for="{{objField.formFieldName}}" class="control-label col-sm-6">{{objField.title}}</label>
<div class="col-sm-6">
<input class="form-control" name="{{objField.formFieldName}}" ng-model="objField.userValue" type="checkbox" ng-value="option" ng-checked="message.question.attributes" />
</div>
</div>
</div>
</div>
<div class="col-sm-12">
<button ng-click="markAsDone(message)" class="btn btn-primary">Done</button>
</div>
<form>
In the controller I'm able to get the formFieldName attribute but I can't figure out how to use it to do the validation.
var MarkAsDone = function(message) {
$scope.includedForm = {};
var formField = message.question.attributes.formFieldName;
if ($scope.includedForm.newRequestForm.{{formField}}.$valid){
//submit the form
}
}
to answer you question:
first, {{}} is === $scope so you don't use that anywhere other than HTML. You use $scope in your JS and {{}} in HTML which creates a pipe (2-way binding) so that $scope.variable.property has bidirectional binding to {{variable.property }} in HTML.
$scope.includeForm.email === {{ includeForm.email }} === ng-model="includeForm.email" === ng-bind="includeForm.email"
if you set anyone of those all are set so if you set $scope it will show up in HTML and obviously as user input gets captured it is already in $scope ... all connected
when attempting to get the value from HTML back into JS you would need create and set a $scope i.e so if you create $scope.dataModel.dataProperty and use that in ng-model=dataModel.dataProperty (example) you again have two way binding ... you don't need to do anything as angular is taking care of the data pipeline. So if you want to extract the value to var, which is probably a waste as the $scope is already set as soon as the user checks the box
var formField = $scope.dataModel.dataProperty;
// but like I said no need as $scope.dataModel.dataProperty; is your var
In JS if you want to use a dynamic property as an object property key you would place the dynamic value in [] e.g.
$scope.variable[dynamicProperty].method;
// you can set a static property as a key with dot notation i.e.
$scope.variable.staticProperty = val;
Hope that helps

Categories