I am creating an editable table from the data that I am getting from the back end now I want to save the data that has been updated.
I have tried using formControl but it only save the data that is in the last column
Here is my code
<form [formGroup]="pagesForm">
<tr *ngFor="let data of pagesArray; let i = index; trackBy: trackByFn">
<td style="text-align: center;" >
<input type="text" formControlName="nameControl" value=[data.name]>
</td>
<td style="text-align: center;">
<input type="text" formControlName="descriptionControl"
vaue=[data.description]>
</td>
</tr>
<button class="btn btn-common" (click)="submit(pagesForm)">Save</button>
</form>
Can anyone help me to save this table data in bulk
In case of reactive form, which I suggest, especially when dealing with an array... with that you need a FormArray to which you push your values when you get them from the backend.
So you can build your form:
constructor(private fb: FormBuilder) {
this.pagesForm = this.fb.group({
arr: this.fb.array([])
})
}
and when you receive your data, in the callback (subscribe or then if you are using promises) call a method, in this example setFormArray() that populates your form array:
setFormArray() {
let arr = this.pagesForm.controls.arr;
this.pagesArray.forEach(x => {
arr.push(this.fb.group({
name: x.name,
description: x.description
}))
})
}
Then you can modify your template to iterate the formarray:
<form [formGroup]="pagesForm" (ngSubmit)="submit(pagesForm.value)">
<div formArrayName="arr">
<tr *ngFor="let page of pagesForm.controls.arr.controls; let i = index"
[formGroupName]="i" >
<td>
<input type="text" formControlName="name">
</td>
<td>
<input type="text" formControlName="description">
</td>
</tr>
</div>
<button type="submit">Save</button>
</form>
Now you end up with an form object that contains property arr, which is an array of your data.
Here's a demo: http://plnkr.co/edit/zfpbUzkvMLOn8CCermGD?p=preview
Hope this helps! :)
Related
I am new to angular .I am stuck with generating or updating the table with respect to text box.
I have a schema which contains 3 fields-country,sales and profit.
There is two text box named as x and y axis.There is a table which should be generated on updating the x and y axis(text box).The should be of two columns in the table which tells what should be in x and y axis.
This is my home.component.html
<div class="form-group">
<form [formGroup]="myFormGroup">
<label>x-axis : </label>
<input type="text" class="form-control" formControlName = "xaxis" > <br>
<label>y-axis : </label>
<input type="text" class="form-control" formControlName ="yaxis" > <br>
<button class="apply-btn" (click)='apply(myFormGroup.value)'>Apply</button>
</form>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>{{this.xaxis}}</th>
<th>{{this.yaxis}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor = 'let product of products' cdkDrag>
<td>{{product.xaxis}}</td> **<----Here is the problem**
<td>{{product.yaxis}}</td>
</tr>
</tbody>
</table>
This is my home.component.ts
export class HomeComponent implements OnInit{
products:any=[];
xaxis:any;
yaxis:any;
myFormGroup:FormGroup;
constructor(private service : ServiceService,private fb : FormBuilder) {
this.CreateForm();
}
//Fetching of data
refreshData(){
this.service.getAll().subscribe((res) => {
this.products=res;
})
}
CreateForm(){
this.myFormGroup=this.fb.group({
xaxis:['',Validators.required],
yaxis:['',Validators.required]
});
}
apply(formValue){
this.xaxis=formValue.xaxis;
this.yaxis=formValue.yaxis;
}
ngOnInit() {
this.refreshData();
}
}
The values should be in the text box is the attributes of schema i.e country,sales and profit.For example, when we enter country and sales for x and y axis respectively then the table should fetch the values of country and sales from the db and update the table with those values.
You can use JavaScript Square Bracket [] notation to access the product object properties dynamically. Square bracket notation accepts expressions, so you can pass the this.xaxis and this.yaxis in your product object. i.e, product[this.xaxis]
<div class="form-group">
<form [formGroup]="myFormGroup">
<label>x-axis : </label>
<input type="text" class="form-control" formControlName = "xaxis" > <br>
<label>y-axis : </label>
<input type="text" class="form-control" formControlName ="yaxis" > <br>
<button class="apply-btn" (click)='apply(myFormGroup.value)'>Apply</button>
</form>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>{{this.xaxis}}</th>
<th>{{this.yaxis}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor = 'let product of products' cdkDrag>
<td>{{product[this.xaxis]}}</td>
<td>{{product[this.yaxis]}}</td>
</tr>
</tbody>
</table>
I am trying to add the rows dynamically for one of the variables which is of type String array in my db. But it only saves the last value entered in the row rather than saving all of them in an array. Below is my view code:
<div class="row" ng-class='{red:true}'>
<label for="remedy">Remedy</label>
</div>
<input name="remedy" id="remedy" ng-model="error.remedy" required>
<br/>
<div class="row" ng-class='{red:true}'>
<a href="#!/errorcreate" class="btn btn-primary btn-small" ng-click="addRemedyRow()" ng-class='{red:true}'>Add Row</a></div>
<br/>
<table style="width:100%">
<thead>
<tr>
<th ng-class='{red:true}'>Remedy</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="rowContent in remedyrows">
<td>{{rowContent.remedy}}</td>
</tr>
</tbody>
</table>
{{error.remedy}}
<button type="submit" class="btn btn-default">Create</button>
Cancel
And this is the code in javascript:
$scope.remedyrows = [];
$scope.addRemedyRow = function() {
$scope.remedyrows.push({
remedy: $scope.error.remedy
});
Below is the output I am receiving (in a screenshot):
I added dsdfg as second row and my final error.remedy value just shows dsdfg rather than showing an array of both values : [wdssdsd,dsdfg]. error is the main document of which remedy is one of the fields of type String array.
Any ideas on how to achieve this?
Instead of error.remedy, which is used as holder for future remedyrow, use intermediate variable output for displaying results and sending them to the server:
Javascript:
$scope.output = $scope.remedyrows.map(function(x) { return x.remedy; });
$http({data: $scope.output, method: 'POST', url: url});
HTML:
{{output | json}}
you could have achieved it by following way:
$scope.remedyrows = [];
$scope.output;
$scope.addRemedyRow = function() {
$scope.remedyrows.push({
remedy: $scope.error.remedy
});
$scope.output = $scope.remedyrows.toString();
}
and in html
{{output}}
I would like to remove item in *ngFor from *ngFor.
When I removed the reply 'test2',
and after I added an other reply, the 'test3' became empty.
<hello name="{{ name }}"></hello>
<form #form="ngForm" (ngSubmit)="submit()" ngNativeValidate class="mt-4">
<div *ngFor="let content of contents; let indexContent = index; let firstContent = first;">
<div *ngFor="let message of content.messages; let indexMessage = index; let firstMessage = first;">
<table>
<thead>
<tr>
<th>Id</th>
<th>Text</th>
<th class="text-right">Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let reply of message.replies; let indexReply = index; let firstReply = first;">
<td [innerHtml]='reply.id'></td>
<td>
<input type="text"
id="{{indexContent}}-{{indexMessage}}-{{indexReply}}-reply-text"
[(ngModel)]=content.messages[indexMessage].replies[indexReply].text
name="[[indexContent]]-[{{indexMessage}}]-[{{indexReply}}]-reply-text">
<br>
<span [innerHtml]="contents[indexContent].messages[0].replies[indexReply].text"></span>
</td>
<td>
<span (click)="message.removeReply(reply)">Remove Reply</span>
</td>
</tr>
</tbody>
</table>
<br>
<span (click)="message.addNewReply()">
Add Reply
</span>
</div>
</div>
<br>
<button type="submit" class="btn btn-primary">Save</button>
</form>
And my message model with different function to add reply, remove reply
message.model.ts
import { Reply } from "./reply";
export class Message {
constructor(public id: number = 0,
public text: string = '',
public replies: any[] = []) {
}
public setModel(obj) {
Object.assign(this, obj);
}
addReply(new_reply) {
this.replies.push(new_reply);
}
addNewReply() {
let new_reply = new Reply();
this.replies.push(new_reply);
}
removeReply(reply) {
this.replies.splice(this.replies.indexOf(reply), 1);
}
}
I reproduce my problem just here: Remove object from array in *ngFor in Angular
https://stackblitz.com/edit/angular-clmi7d
I would use trackBy option to avoid unexpected situations
html
<tr *ngFor="let reply of message.replies; trackBy: trackByFn;
^^^^^^^^^^^^^^^^^^
app.component.ts
trackByFn(i: number) {
return i
}
What you need here, is standalone ng-model option. Also, I would simplify ngModel and innerHtml bindings
<input type="text"
id="{{indexMessage}}-{{indexReply}}-reply-text"
[(ngModel)]="reply.text"
[ngModelOptions]="{standalone: true}"
name="{{indexMessage}}-{{indexReply}}-reply-text" />
<span [innerHtml]="reply.text"></span>
Another option would be using ngFor trackBy as it was suggested by #yurzui, in that case standalone is not needed.
You also have an issue with Reply.id. As a temporary workaround you may try following approach:
let maxId = 0;
export class Reply {
constructor(public id: number = 0, public text: string = '') {
this.id = this.id || ++maxId;
}
}
Before doing a form submit for my MVC webpage, I want to see a list of key fields to the controller. However the parameter on the controller is null.
My JavaScript is as follows;
$("#savePropertiesToBeAssigned").click(function (e) {
e.preventDefault();
var $propertyIds = $("#propertyRows").find($(".selectProperty:checked"));
var propertyIds = [];
$.each($propertyIds, function () {
var propertyId = $(this).data("msurvey-property-id");
propertyIds.push(propertyId);
});
var $form = $(this).closest("form")[0];
$form.action += "?PropertyIds=" + propertyIds + "&page=" + GetHiddenField("msurvey-page");
$form.submit();
});
The MVC Action is;
[HttpPost]
public async Task<ActionResult> AddFromContract(int[] PropertyIds, int? page)
{
var pageNumber = page.GetValueOrDefault(1);
return RedirectToAction("AddFromContract", new { page = pageNumber });
}
The PropertyIds comes through wrongly as null, whilst the page has the correct value.
EDIT - I am displaying the View in response to a comment:
#{
ViewBag.Title = "Add properties from the contract into the survey";
}
#using (Html.BeginForm("AddFromContract", "PropertySurvey", FormMethod.Post))
{
<div id="hiddenFields"
data-msurvey-page="#ViewBag.Page"></div>
<input type="hidden" id="propertyIds" name="propertyIds" />
<fieldset>
<legend>#ViewBag.Title</legend>
#if (ViewBag.PagedList.Count > 0)
{
<section id="PropertyList" style="margin-right: 28px;">
<p>
The properties below have already been added to the contract <b>#SessionObjectsMSurvey.SelectedContract.ContractTitle</b>, but are NOT in this survey
<b>#SessionObjectsMSurvey.SelectedContract.SurveyTitle.</b>
</p>
<table class="GridTbl">
<thead>
<tr>
<th>UPRN</th>
<th>Block UPRN</th>
<th>Address</th>
<th style="text-align: center;">
Select All<br/>
<input type="checkbox" id="selectAll"/>
</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="3" style="text-align: right;">Select the properties you want to include in the survey, and click on the Save button.</td>
<td style="text-align: center;">
<button id="savePropertiesToBeAssigned" class="btn-mulalley">
Save
</button>
</td>
</tr>
</tfoot>
<tbody id="propertyRows">
#foreach (var property in ViewBag.PagedList)
{
<tr>
<td>#property.UPRN</td>
<td>#property.BlockUPRN</td>
<td>#property.Address</td>
<td style="text-align: center;">
<input type="checkbox"
name="selectProperty"
class="selectProperty"
data-msurvey-property-id="#property.PropertyId"/>
</td>
</tr>
}
</tbody>
</table>
#Html.PagedListPager((IPagedList) ViewBag.PagedList, page => Url.Action("AddFromContract", new {page}))
</section>
}
else
{
<p>Either no properties have been entered, or all of them have been assigned to the survey.</p>
}
</fieldset>
}
#section scripts
{
#Scripts.Render("~/bundles/page/propertyTransfer")
}
There is no need for any javascript to solve this. You already have checkbox elements in you form, and if they have the correct name and value attributes, they will bind to your model in the POST method.
Delete the <input type="hidden" id="propertyIds" name="propertyIds" /> and change the the view to
#foreach (var property in ViewBag.PagedList)
{
<tr>
<td>#property.UPRN</td>
....
<td style="text-align: center;">
<input type="checkbox" name="propertyIds" value="#property.PropertyId" />
</td>
</tr>
}
and delete the script.
When the form submits, the value of all checked checkboxes will be sent to the controller, and because the checkbox has name="propertyIds" they will bind to your int[] PropertyIds in the POST method.
You will also need to change <div id="hiddenFields" data-msurvey-page="#ViewBag.Page"></div> to
<input type="hidden" name="page" value="#ViewBag.Page" />
Side note: I recommend you start using view models rather that using ViewBag, including a view model for the collection, which would contain properties int ID and bool IsSelected so that you can strongly type your view to your model.
Angularjs code.
$scope.model;
I defined my new object in angular like following
$scope.newRelation = {
relationType : null,
relatedModel : null
};
HTML
<div class="table-responsive">
<table class="table">
<tr>
<td><select class="form-control"
ng-model="newRelation.relationType" required
ng-options="modeltype as modeltype for modeltype in modeltypes"></select>
</td>
<td><select class="form-control"
ng-model="newRelation.relatedModel" required
ng-options="model.name as model.name for model in models"></select>
</td>
<td>
<button type="button" class="btn btn-default"
ng-click="addRelation()">Add Relation</button>
</td>
</tr>
</table>
</div>
Angular code
$scope.addRelation = function()
{
$scope.model.relations.push($scope.newRelation);
};
When i click form save the model.relations values are empty in the back end.Any clues ? Please let me know if i need to provide more information
You directly assigned object to list.
It'll keep reference.
make a copy
var obj=angular.copy($scope.newRelation);
Then push it
$scope.model.relations.push(obj);