Angular2 - How to use one way databinding to bind to JSON attribute - json

I try to use Angular2's one way databinding to bind an input field value to a JSON property.
The JSON object looks like this:
[
{
"name": "my name",
"list": [
{
"date": "0101970",
"list": [
{
"timespan": "6-7",
"entries": [
{
"name": ""
},
{
"name": ""
},
{
"name": ""
}
]
}
]
}
]
}
]
I want to bind the value to a specific name attribute of entries.
This is how I try to do the on way binding:
<div class="col-md-4" *ngFor="#category of categories">
<div>
<div class="col-md-12">
<h1>{{category.name}}</h1>
</div>
</div>
<div *ngFor="#listentry of category.list">
<div class="row">
<div class="col-md-12">
<h2>{{listentry.date}}</h2>
</div>
</div>
<div class="row" *ngFor="#shift of listentry.list">
<div class="row">
{{shift.timespan}}
</div>
<div class="row" *ngFor="#entry of shift.entries">
<div class="col-md-10">
<input type="text" class="form-control" (ngModel)="entry.name">
</div>
</div>
</div>
</div>
</div>
This is my Component:
export class InputComponent {
public categories:Category[];
constructor(private _dataService:DataService) {
// ... fetch data from the service here
}
}
As I understand the databinding in Angular2 (ngModel)="attribute" binds from the view to the model and [ngModel]="attribute" binds the other way around.
So, what is wrong with my <input type="text" class="form-control" (ngModel)="entry.name"> then?
I could use two way databinding instead of course, but I have some other constraints (disabling form elements) which just apply after a button was pressed and not on the user input.

With
<input type="text" class="form-control" [ngModel]="entry.name">
you bind the JSON value to the input.
(ngModelChange)="model=$event" updates the model when an `ngModelChange` event is emitted.
[(ngModel)]="model" binds two-way
where ngModel is a directive with an
#Input() ngModel; // for the [ngModel]="..." bindign
#Output() ngModelChange = EventEmitter(); // for the (ngModelChange)="..." binding
together they support the shorthand form
[(ngModel)]="model"

Related

Refresh cdkDropListData after dialog closes and element is removed from array

I have the angular drag and drop list set up like this:
<div class="row col-12" *ngFor="let level of team_heirarchy; let i=index;">
<div class="col-3">
<p>{{level.name}}</p>
</div>
<div class="col-9">
<div cdkDropList id="{{level.key}}" [cdkDropListData]="level.value"
[cdkDropListConnectedTo]="heirarchy_levels" class="example-list" [cdkDropListDisabled]="level.disabled" (cdkDropListDropped)="drop($event)">
<div class="example-box" *ngFor="let item of level.value; let j=index;" cdkDrag>{{item}}
<button *ngIf="!level.disabled" mat-icon-button color="warn" (click)="deleteHeirarchyItem(i, j)"><mat-icon>cancel</mat-icon>
</button></div>
</div>
</div>
</div>
And the TS File:
team_heirarchy = [
{
"name": "Level 1(ROOT)",
"key": "level1",
"value": ["Administration"],
"disabled": true
},
{
"name": "Level 2",
"key": "level2",
"value": ["Lead HR", "Lead Manager"],
"disabled": false
}
]
deleteHeirarchyItem(i: number, j: number){
this.dialog.open(ConfirmDialogComponent, {
height: "auto",
width: "500px",
data:{
message: "Are you sure you want to delete this position.",
}
}).afterClosed().subscribe(result => {
if(result){
this.team_heirarchy[i].value.splice(j, 1)
}
})
}
The problem is, if I place this line
this.team_heirarchy[i].value.splice(j, 1)
outside the mat-dialog, the ui is updated instantly and the element is removed from the drop list.
But if I continue this way and wait till the user confirms and dialog closes with a positive result, the element is removed from the array but on the ui the element is still there until I try to drag an element.
Any help?

How to validate dynamic form in angular

Below is my code. According to the loop count, fName will increase. currently I am using ng-bootstrap-form-validation for validation. The issue if I validate using formControlName that's not working it's duplicating if one field(fName) is correct all the fields become correct.
Below is my HTML code,
<form [formGroup]="formGroup" (validSubmit)="onSubmit()">
<div class="results-traveler" *ngFor="let item of createLoopRange(selectedDataDetails[0].Adult); let ii= index;">
<div class="row">
<div class="col-12 heading" *ngIf="ii == 0 else travelCount">Traveller {{ii}}</div>
<div class="col-6 M-full-div">
<label>First Name</label>
<div class="inputContainer form-group">
<input class="InputField form-control" type="text" placeholder="Type Here" formControlName="fName" id="fName_{{i}}" />
</div>
</div>
</div>
</div>
<a class="SearchBtn" type="submit">CONTINUE</a>
</form>
Below is the typescript code I am using,
ngOnInit(): void {
this.bookingDetailsValidate();
}
bookingDetailsValidate() {
this.formGroup = new FormGroup({
fName: new FormControl('', [
Validators.required,
Validators.pattern(/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/)
])
});
}
onSubmit() {
console.log(this.formGroup);
}
Can anyone please guide me on how to do this. Thanks.
for fName use FormArray then add FormControl to FormArray and you can validate FormArray and every FormControl inside FormArray
example:
this.formGroup = new FormGroup({
fName: new FormArray([], [
Validators.required
])
});
...
const arrayOfNameControl = this.formGroup.get('fName') as FormArray
arrayOfNameControl.insert(0, new FormControl('',[
Validators.required,
Validators.pattern(/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/)
]));
You need form array here. Please refer the link.
Angular form array

How to display complex object attributes in form fields in angular

I have a JSON object with the structure below which I want to display in a form for edit when I clicked on edit button on my table row.
json
{
"id": 123,
"name": "Paul",
"cars": [
{
"type": "toyota",
"year": "2013"
},
{
"type": "audi",
"year": "2010"
}
]
}
I need help on how to display the type of each cars object in my table file separated by comma(,). I got the object id and name displayed but not attributes in the cars array. How do I complete my .ts code so that the type of each car could show up in my table.
.ts
showRowDetail( data: any ) {
this.formData.controls.id.setValue( data.id );
this.formData.controls.name.setValue( data.name );
//how do I update here to set the cars: types
}
.html
<div class="form-group">
<label>ID:</label> <input type="text" class="form-control"
formControlName="id">
</div>
<div class="form-group">
<label>Name:</label><input type="text" class="form-control"
formControlName="name">
</div>
<div class="form-group">
<label>Cars:</label> <input type="text" class="form-control"
formControlName="cars">
</div>
I omitted the *ngFor table code, the showRowDetail function is used on the edit button, which when clicked it opens a modal that present the form where the object data are shown. data.id and data.name fields are working, just the cars type I needed help with.
how to display the "type" of each "cars" object in my table file
seperated by comma(,)
Generate comma separated type values from carsarray using map() and join().
showRowDetail( data: any ) {
this.formData.controls.id.setValue( data.id );
this.formData.controls.name.setValue( data.name );
let types = data.cars.map(car => car.type).join(", ");
this.formData.controls.cars.setValue(types);
}

How to bind JSON object key value pair separately to angular template

And i have a dynamic json coming from an API as below:
{123: "Mumbai", 456: "Bangalore", 789: "Chennai", 101: "Andhra",...}
I have below HTML code written in my template in which I am getting image-1, image-2 logos from my assets folder
<div class="row">
<div class="col-6" (click)="cityClick('Bangalore')">
<div class="img-1">
// my image-1 logo goes here
</div>
<div class="img-text">
Bangalore
</div>
</div>
<div class="col-6" col-6 (click)="cityClick('Mumbai')">
<div id="image_block" class="img-2">
// my image-2 logo goes here
</div>
<div id="text_block" class="img-text">
Mumbai
</div>
</div>
</div>
How to get the key of the json when i click on image and also display the text below the image from the corresponding key-value.
And when i click i should be able to pass the key and text inside the click event. Please help as i am new to Angular!
First convert this JSON into an JavaScript/TypeScript array like below:
var json = {123: "Mumbai", 456: "Bangalore", 789: "Chennai", 101: "Andhra" };
var jsonToBeUsed = [];
for (var type in json) {
item = {};
item.key = type;
item.value = json[type];
jsonToBeUsed.push(item);
}
This will result in data like below:
Now use NgFor in the template to bind array. (Refer this for NgFor)
<div class="row">
<div *ngFor="let item of array">
<div class="col-6" (click)="cityClick(item)">
<div class="img-1">
// my image-1 logo goes here
</div>
<div class="img-text">
{{item.value}}
</div>
</div>
</div>
</div>
We have passed the whole object in the click event. You can read any of the desired property from the object in click event handler which you will write in the component.
For your special requirement, you can use below markup:
<div class='row' *ngFor='let index of array; let i = index; let even = even'>
<div *ngIf="even" class='col-6' (click)="cityClick(array[i])">
<div class="img-1">
// my image-1 logo goes here
</div>
<div class="img-text">
{{array[i].value}}
</div>
</div>
<div *ngIf="!even" class='col-6' (click)="cityClick(array[i])">
<div class="img-1">
// my image-1 logo goes here
</div>
<div class="img-text">
{{array[i].value}}
</div>
</div>
</div>
Use this below function in your code:
getKeyByValue(object, value) {
return Object.keys(object).find(key => object[key] === value);
}
And use as
var dynamicJson = {123: "Mumbai", 456: "Bangalore", 789: "Chennai", 101: "Andhra"}
cityClick(value){
var key = this.getKeyByValue(this.dynamicJson, value);
console.log(key);
}
{123: "Mumbai", 456: "Bangalore", 789: "Chennai", 101: "Andhra",...}
Do you have influence on that JSON? This highly looks like a design issue for me. I assume those numbers are id's. I believe somethings like this should be better:
[{id: "123", name: "Mumbai"}, {id: "456", name: "Bangalore"}, {id: "789", name: "Chennai"}, {id: "101", name: "Andhra"},...}]
In that case you receive an array of cities, which could be an interface to parse to.
export interface City {
id: string;
name: string;
}
And you can easily render it in html by using *ngFor
<div *ngFor="let city of cities">
<!--do something with city.id and city.name-->
</div>
<div *ngFor let value of json |keyvalue > </div>

How to display Date in Html using angular?

I have two datepickeres in my HTML file using bootstrap and I am trying to display a simple message from this (first selected date) to this (second selected date).
The typescript class is:
export class Datepicker {
date: any;
}
And the HTML is:
<div class="form-group">
<label for="hireDate">Hire Date:</label>
<form class="form-inline">
<div class="form-group">
<div class="input-group">
<input class="form-control" name="hireDate" id="hireDate" placeholder="yyyy-mm-dd"
[(ngModel)]="datePicker.date" ngbDatepicker #d="ngbDatepicker">
<div class="input-group-append">
<button class="btn btn-outline-secondary" (click)="d.toggle()" type="button">
<img src="/assets/calendar-icon.svg" style="width: 1.2rem; height: 1rem; cursor: pointer;"/>
</button>
</div>
</div>
</div>
</form>
</div>
<div>{{datePicker.date}} </div>
But it gives me Object as a result and not the selected date.
Any help?
You can use Angular DatePipe to convert the date into the format that you wish to display.
<div> {{datePicker.date | date : 'yyyy-MM-dd'}} </div>
DEMO
EDIT : To convert the date object ({ "year": 2018, "month": 8, "day": 7 }) returned by ngBootstrap to yyyy-MM-dd format, you can do the transformation inside the setter method of datePicker.date property as follows :
module.ts
providers : [DatePipe]
service.ts
export class Datepicker {
_date: string;
constructor(private datePipe: DatePipe) {}
set date(value) {
let date = new Date(value.year, value.month, value.year);
this._date= this.datePipe.transform(date, 'yyyy-MM-dd');
}
get date() {
return this._date
}
}
ng-bootstrap datepicker selection gives you an object like this
Model: {
"year": 2018,
"month": 8,
"day": 9
}
You can write initialize a date object from selected model like this:
<div> {{ new Date(datePicker.date.year, (datePicker.date.month - 1), datePicker.date.day) | date : 'yyyy-MM-dd'}} </div>
https://ng-bootstrap.github.io/#/components/datepicker/examples#popup