I am new in angular.when I try to fetch value of textbox which is in a loop getting json array in this format - in the formsubmit method if I do console.log(myForm.value) then I get this big list:
[
{
"product-0":"Voltas",
"avi-0":"5",
"def-0":"0",
"quantity-0":3,
"product-1":"Voltas1",
"avi-1":"5",
"def-1":"1",
"quantity-1":3,
"product-2":"Voltas2",
"avi-2":"5",
"def-2":"7",
"quantity-2":1,
"product-3":"Voltas0",
"avi-3":"5",
"def-3":"6",
"quantity-3":1
}
]
can anyone help me to get json in this format
[
{
"product-0":"Voltas",
"avi-0":"5",
"def-0":"0",
"quantity-0":3
},
{
"product-1":"Voltas",
"avi-0":"5",
"def-0":"0",
"quantity-0":3
}
]
in angular 2.thanks in advance.
this is my html code
<form #myForm="ngForm">
<ion-item *ngFor="let productlist of productlists;let i = index">
<ion-row>
<ion-col width-15><input type="hidden" id="hiddenValue" [(ngModel)]="productlist.product" name="product-{{i}}" /> {{productlist.product}}</ion-col>
<ion-col width-25><input type="hidden" id="hiddenValue" [(ngModel)]="productlist.avi" name="avi-{{i}}"/>{{productlist.avi}}</ion-col>
<ion-col width-25><input type="hidden" id="hiddenValue" [(ngModel)]="productlist.def" name="def-{{i}}"/>{{productlist.def}}</ion-col>
<ion-col width-25 class="add">
<button clear (click)="increment(productlist)"><ion-icon name="add-circle" ></ion-icon></button>
<ion-item><ion-input type="number" [(ngModel)]="productlist.quantity" name="quantity-{{i}}" class="quantity"></ion-input></ion-item>
<button clear (click)="decrement(productlist)"><ion-icon name="remove-circle" ></ion-icon></button>
</ion-col>
</ion-row>
</ion-item>
<button ion-button (click)="formSubmit(myForm.value)">submit</button>
</form>
In your template, you have a single form and a single form can only return a single object.
If you want to split that into multiple objects then you need to write code in your submit method to iterate through 'the big list' and create the structure you want:
[ {} ] => [ {}, {}. {} ]
You would have to search for object.keys.substr(0,6) matching 'product'. This is not a good idea even though it could work.
What I suggest instead is that you create nested forms - one form for each item in the list. I suggest you use reactive forms for this, as it is much easier.
This is because you need to create a component that represents a single product in your array:
[ {}, {}, {} ] =
[{product-item}, {product-item}, {product-item}]
ie:
ProductList-Component has a form with ngFor and inside the ngFor you load multiple children, like this:
<product-item *ngFor="let product of productlists"
[product]="product">
</product-item>
Notice that this product-item component will need an #Input, which is a formGroup, eg:
#Input()
public product: FormGroup;
See this article to learn how you can build this.
In particular see the section that starts with "First idea is obviously to extract a component that represents a single item in the form array"
Related
I am having trouble with a todo list. I have an array of tasks, when a task is checked it has to show a title 'Finished tasks' and the task checked has to move to the bottom to finished tasks and have a line through it, if unchecked, it moves back to the top. To solution this I need to use Pipes, however, I am having a hell of a time and can't work it out as the tasks are always duplicated when I add the pipe, so obviously I am adding it wrong.
home.html
<ion-item-sliding *ngFor='let tarea of servicio.tareas | sort'><ion-item ><ion-checkbox color="dark" checked='false' (click)='checked(tarea)'></ion-checkbox>
<div id="padding"><b>{{tarea.nombre}}</b></div>
</ion-item>
<ion-item-options side="end">
<button ion-button color="primary" (click)="editar(tarea.nombre, index)" >
<ion-icon name="text"></ion-icon>
EDITAR
</button>
<button ion-button color="secondary" (click)="eliminar(tarea)">
<ion-icon name="call"></ion-icon>
BORRAR
</button>
</ion-item-options>
</ion-item-sliding>
</ion-list>
<ion-list>
<label \\\*ngIf='(servicio.tareas | filter).length > 0'>Tareas terminadas</label>
</ion-list>
As you can see, I have the original sliding list with the pipe that sorts from important to non important, which works fine, then I have the finished tasks lavel which will appear when the length of the filter pipe is more than 1.
Here is the filterPipe:
name: 'filter'
})
export class FilterPipe implements PipeTransform {
transform(tarea1: Tareas[]) {
return tarea1.filter(tarea2 => tarea2.finalizada);
}
}
This should be the result, when a task is marked it shows the finished tasks and the finished result underneath, when unchecked, it goes back at the top.
List with finished tasks showing
This is the last thing I need to do and it is literally driving me mad.
It seems you're having trouble with mutating an object. By default, if you just switch the value of one of the attributes of one of the objects of your list, the sort pipe won't be executed again:
const tarefas = [
{
id: 't1',
finalizada: false,
},
{
id: 't2',
finalizada: true,
}
];
In the code above, if you do tarefas[0].finalizada = true (or any other change in any attribute of a random tarefas[index]), the sort pipe won't run again.
What you can do is add a parameter to your pipe: pure: false. Then it will track changes to the objects inside your array:
#Pipe({
name: 'sort',
pure: false
})
export class SortPipe...
As an alternative, you can change the reference of your tarefas object when you change an object attribute value:
tarefa.finalizada = true;
servico.tarefas = [...service.tarefas];
What I want to do is to show the first row in HTML (in below code) only on a condition using *ngIf. I've done the basic validation, but I have a hard time and can't find how I can do *ngIf accessing an object key directly (and not using an *ngFor before, on a different element). What I want to do is to show the row only when object.key === 'specific value'. I've tried options with "keys", "Object" but nothing seems to work. If you guys have any suggestion I would appreciate it.
my HTML
<ion-grid *ngIf="!isLoading && loadedBio.length > 0">
<ng-container *ngFor="let bio of loadedBio">
<ng-container *ngIf="bio.category === '1'">
<ion-row class="ion-padding">
<ion-col>
<ion-list class="no-last-border">
<ion-item
detail="false"
*ngIf="bio.category === '1'">
<ion-label
[ngStyle]="{
color:
bio.category === '1'
? '#A34F83'
: 'var(--ion-color-secondary)'
}"
class="bioLabel"
>{{bio.friendlyName}}</ion-label>
</ion-item>
</ion-list>
</ion-col>
</ion-row>
</ng-container>
</ng-container>
my JS object I want to access the key from has the following format
loadedBio = [{key: value, key2: value}]
If you need to display each of the properties on your found object, then, in your component, you'd want to convert that found object to an array first - where each element in the array represents a property on the object, and then iterate over it to display each element:
const obj1 = {
a: 'a title',
b: 42,
c: false
};
const arr1 = Object.values(obj1);
Put together that would look like this. First, in the component:
import { Component } from '#angular/core';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
loadedBio = [{'key': 1, 'key2': 2}, {'key': 3, 'key2': 4}];
obj1 = {
a: 'a title',
b: 42,
c: false
};
arr1 = Object.values(this.obj1);
constructor() {
}
}
And in the view:
<ion-header>
<ion-navbar>
<ion-title>Home</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ng-container *ngIf="loadedBio">
<ion-list *ngFor="let bio of loadedBio">
<ng-container *ngIf="bio.key === 1">
<ion-item>{{bio.key}}</ion-item>
</ng-container>
</ion-list>
</ng-container>
<ng-container *ngFor="let e of arr1">
<ion-item>{{e}}</ion-item>
</ng-container>
</ion-content>
This should give you an idea of how to handle your situation. Sounds like you want to transform the data in your component, and then iterate over it need be in your view.
Here is a link to the working StackBlitz:
Best to filter your array in the controller, like this
this.filteredBio = this.loadedBio.filter(el=>el.category === 'overall')
and then simply use *ngFor on this filteredBio array. This will only have the objects whose category is 'overall'
Another solution is to implement a custom pipe Refer this
What I want to achieve in the end is to see which form controls individually from my form have changed and to create a new object with them assigning a boolean of true if they were changed or false if not. Please see below what I achieved so far, but I don't think this the right approach as when I'll have more from controls my method will become gigantic. If you guys have any idea how I can approach this I would really appreciate it.
my Html
<form [formGroup]="editProfileForm">
<ion-row>
<ion-col>
<ion-input
*ngIf="profileData"
formControlName="firstName"
placeholder="First Name"
></ion-input>
</ion-col>
</ion-row>
<ion-row>
<ion-col>
<ion-input
*ngIf="profileData"
formControlName="lastName"
placeholder="Last Name"
></ion-input>
</ion-col>
</ion-row>
</form>
<ion-fab
vertical="bottom"
horizontal="center"
slot="fixed"
(click)="onSubmitEditedProfile()">
<ion-fab-button>
<ion-icon name="checkmark-outline"></ion-icon>
</ion-fab-button>
</ion-fab>
my TS
onSubmitEditedProfile() {
if (this.profileData !== null) {
let updatedFirstName = this.editProfileForm.get("firstName").value;
if (this.profileData.firstname !== updatedFirstName) {
}
console.log("it's the same");
} else {
console.log("it's different")
}
And as my approach means, I'll do the same for lastName and so on
Here is the example where you can iterate each form control and identify either its changed or not based on that populate new array of object.
onSubmitEditedProfile() {
const formValue = [];
// iterate over form controls no matter how many control you have.
Object.keys(this.form.controls).map((key) => {
// create a new parsed object
const parsedValue = {
[key]: this.form.get(key).value, // key is the actual form control name
changed: this.form.get(key).dirty // added changed key to identify value change
}
// push each parsed control to formValue array.
formValue.push(parsedValue)
})
console.log(formValue)
}
Here is the stackblitz working DEMO
Hope this address your requirements.
All you need is just to read dirty value on FormGroup or individual FormControl
https://angular.io/api/forms/AbstractControl#dirty
onSubmitEditedProfile() {
if (!this.editProfileForm.dirty) {
console.log("it's the same");
} else {
console.log("it's different")
}
I have a long text to be translated in an Ionic 4 app. I am using angular ngx-translate (#ngx-translate v11.0.1).
To improve readability I would like to have the translation in multiples lines instead of one.
I have changed my i18n json, from this (en-US.json):
"common-questions-content" : "<b>Question 1?</b> Answer 1 <br> <b>Question 2?</b> Answer 2 <b>Question 3?</b> Answer 3",
To this:
"common-questions-content" : [
"<b>Question 1?</b> Answer 1 <br>",
"<b>Question 2?</b> Answer 2 <br>",
"<b>Question 3?</b> Answer 3"
],
Unexpectedly this works! But, it puts commas between every value of the array:
I load the translation service in my app.component.ts:
import {TranslateService} from '#ngx-translate/core';
...
private translateService: TranslateService,
...
this.translateService.use('en-US');
Finally I use it in my html page like that:
{{ 'common_questions' | translate }}
Is it possible to change this behavior and just show all the text without commas?
I would recommend you to have one single input per statement and without html tags int the translations such as :
in your en.JSON :
"QUESTION_1":"blabla",
"QUESTION_2":"blabla",
"QUESTION_3":"blabla",
"ANSWER_1":"blabla",
"ANSWER_2":"blabla",
"ANSWER_3":"blabla",
Then in your component, create two class properties of type array like so:
public questions : string[];
pulbic answers : string[];
constructor (private translate: TranslateService) {
translate.get(["QUESTION_1", "QUESTION_2", "QUESTION_3"]).subscribe(
values => {
this.questions = Object.keys(values).map(key => values[key]);
}
);
translate.get(["ANSWER_1", "ANSWER_2", "ANSWER_3"]).subscribe(
values => {
this.answers = Object.keys(values).map(key => values[key]);
}
);
}
Then in your html display, customize, add click events or whatever you need :
<ion-grid>
<ion-row>
<ion-col col-6>
<ion-grid>
<ion-row *ngFor="let q of questions"><b>{{q}}</b></ion-row>
</ion-grid>
</ion-col>
<ion-col col-6>
<ion-grid>
<ion-row *ngFor="let a of answers">{{a}}</ion-row>
</ion-grid>
</ion-col>
</ion-row>
</ion-grid>
This is basic html implementation, but you see how much potential is left for you to use. you can define click events, animations, colors, selected items and so on..
A more cleaner solution which:
avoids creating unhanded subscriptions,
is reusable across different components
is to create a custom pipe:
import { Pipe, PipeTransform } from '#angular/core';
import { TranslatePipe } from '#ngx-translate/core';
#Pipe({
name: 'translateList',
})
export class TranslateListPipe extends TranslatePipe implements PipeTransform {
transform(key: any, length: number): any[] {
return [...Array(length).keys()].map((i) => super.transform(`${key}.${i}`));
}
}
And use it like so in any template with help of NgFor:
<div *ngFor="let item of 'TRANSLATION_KEY' | translateList:2">{{ item }}</div>
Where TRANSLATION_KEY is key that hols an array of 2 strings:
"TRANSLATION_KEY": ["hello", "world"]
This custom pipe extends ngx-translate's own TranslatePipe and uses super.transform(...) call under the hood, so all translation heavy lifting is still handled by ngx-translate.
I am creating a list of text and buttons.
I want to identify which button has been clicked in the clicked function.
HTML
<ion-list>
<ion-item ion-item *ngFor="let act of questions.test">
{{act.quote}}
<ion-item>
<button large ion-button item-right (click)="SelectClicked()">Select</button>
</ion-item>
</ion-item>
</ion-list>
TS
this.questions =
{
"test":[
{
"quote": "first row"
}, {
"quote": "second row"
}, {
"quote": "third row"
}, {
"quote": "fourth row"
}
]
}
SelectClicked(detail){
//please help
// which button was clicked ( 1 to 4 ) ?
console.log("SelectClicked" + detail);
}
I am guessing that you want the selected element, that can be done by passing on the event to the handler.
<button (click)="SelectClicked($event)"></button>
Which you can fetch the element from the event with using event.target, event.srcElement or event.currentTarget.
But if you mean to pass along the index or the item of your array, you can just pass on that act object or add an index to the loop...
<ion-item ion-item *ngFor="let act of questions.test; let i = index">
...and pass on the object...
<button (click)="SelectClicked(act)"></button>
...or the id
<button (click)="SelectClicked(i)"></button>
You can pass the "act" value as a parameter to the function like this
<button large ion-button item-right
(click)="SelectClicked(act)">Select
</button>