How to implement nested data form in angular2 - json

Here is Json schema :
{
"_id" : ObjectId("59031d77fd5e1c0b3c005d15"),
"resume_data" : {
"work_experience" : [
{
"company" : "example",
"website" : "example.com",
"position" : "Internship",
"highlights" : "Learn To Create API In Laravel Framework. and also Learn Angular 2 for Front end Development.",
"project_experience" : [
{
"projectName" : "Fb Project",
"teamMember" : "5",
"technology" : "PHP,Laravel-5,Angular-2,MongoDb",
"projectPosition" : "Back-end Developer"
}
]
}
]
}
}
Here is image:
I have reference of this answer but i don't know about nested form data. can anyone explain how to implement it.

Here is your code, which sets the data you are receiving from backend, here I have stored it in a variable data.
Please notice, this is a shortened version of your form, but the basics are there, you only need to add the few missing properties in corresponding form arrays.
The build of the empty form looks is just a FormArray named work_experience matching your json structure:
this.myForm = this.fb.group({
work_experience: this.fb.array([])
})
We add the fields when you are receiving the data, call a function called setWorkExperience in the callback when receiving data:
setWorkExperience(){
// get the formarray
let control = <FormArray>this.myForm.controls.work_experience;
// iterate the array 'work_experience' from your JSON and push new formgroup with properties and the inner form array
this.data.work_experience.forEach(x => {
// add the rest of your properties also below
control.push(this.fb.group({company: x.company, project_experience: this.setFormArray(x)}))
})
}
setFormArray is called from the previous function, where we patch the data with from project_experience to the inner form array:
setFormArray(x) {
// create local array which is returned with all the values from the 'project_experience' from your JSON
let arr = new FormArray([])
x.project_experience.map(y => {
// add the rest of your properties below
arr.push(this.fb.group({projectName: y.projectName}))
})
return arr;
}
The template would then look like this:
<form [formGroup]="myForm">
<!-- Outmost array iterated -->
<div formArrayName="work_experience">
<div *ngFor="let a of myForm.get('work_experience').controls; let i=index">
<h3>COMPANY {{i+1}}: </h3>
<div formGroupName="{{i}}">
<label>Company Name: </label>
<input formControlName="company" /><span><button (click)="deleteCompany(i)">Delete Company</button></span><br><br>
<!-- inner formarray iterated -->
<div formArrayName="project_experience">
<div *ngFor="let b of myForm.controls.work_experience.controls[i].controls.project_experience.controls; let j=index">
<h4>PROJECT {{j+1}}</h4>
<div formGroupName="{{j}}">
<label>Project Name:</label>
<input formControlName="projectName" /><span><button (click)="deleteProject(a.controls.project_experience, j)">Delete Project</button></span>
</div>
</div>
<button (click)="addNewProject(a.controls.project_experience)">Add new Project</button>
</div>
</div>
</div>
</div>
</form>
In the template you can see the buttons for add and delete of projects and companies. Adding and deleting companies are straightforward, where initCompany() returns a formGroup:
deleteCompany(index) {
let control = <FormArray>this.myForm.controls.work_experience;
control.removeAt(index)
}
addNewCompany() {
let control = <FormArray>this.myForm.controls.work_experience;
control.push(this.initCompany())
}
In the add project we pass as parameter from the template the current formArray control, to which we just push a new FormGroup:
addNewProject(control) {
control.push(this.initProject())
}
In the delete function we pass the current formarray as well as the index of the project we want to delete:
deleteProject(control, index) {
control.removeAt(index)
}
That should cover pretty much everything.
Plunker

Please Check it Out This
Plunker Here
Json Store Like This
{
"name": "",
"work_experience": [
{
"name": "asdasd",
"project_experience": [
{
"Project_Name": "asdasdasd"
},
{
"Project_Name": "asdasdasd"
}
]
}
]
}

Related

How can i access to 'download_url' in json using vuejs

i want to get 'tags' and 'download url' inside meta, i cannot access to 'download_url' in json how i can do it, as a backend i am using wagtail cms, and is it good to use vue js with wagtail cms (headless cms)
<template>
<div>
<div>
<b-card-group deck v-for="item in results" :key="item.id">
<b-card
>
<b-card-text>
<div v-for="block in item.body" :key="block.id">
<div v-if="block.type == 'heading'">
<h2>{{block.value}}</h2>
</div>
<div v-if="block.type == 'image'">
<img :src="'http://127.0.0.1:8000/api/v2/images/' + block.value">
</div>
<div v-if="block.type == 'paragraph'">
<h2 v-html="block.value">{{block.value}}</h2>
</div>
</div>
</b-card-text>
>
</b-card>
</b-card-group>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'Home',
data () {
return {
results: null
}
},
mounted () {
axios.get('http://127.0.0.1:8000/api/v2/pages/?type=news.NewsPage&fields=intro,body,image_thumbnail')
.then((response) => (
this.results = response.data.items
))
}
}
</script>
here is json api. i accessed till id of image and do not know what to do next
{
"id": 3,
"meta": {
"type": "wagtailimages.Image",
"detail_url": "http://localhost/api/v2/images/3/",
"tags": [
"gadget",
"phone",
"samsung"
],
"download_url": "/media/original_images/affordable_new_9-7-inch_ipad_group_fan2_1_1.png"
},
"title": "affordable_new_9-7-inch_ipad_group_fan2 1 (1).png",
"width": 528,
"height": 357
}
Assuming that response.data.items is the json you showed above, you can extract just what you want from there. Right now, your component has the whole response.data.items in this.response. So if you want all of it there, keep that.
You can store the 'tags' and 'download url' to your component by adding two new pieces of data ie add them to your data:
data () {
return {
results: null,
tags: null,
downloadUrl: null
}
... and then set those in your response block from the request's response like this.
mounted () {
axios.get('http://127.0.0.1:8000/api/v2/pages/?type=news.NewsPage&fields=intro,body,image_thumbnail')
.then((response) => (
this.results = response.data.items
this.tags = response.data.items.meta.tags
this.downloadUrl = response.data.items.download_url
))
}
When dealing with nested response objects like this, it can help to make a local variable with the data and add it that way. Also, if you dont need the whole response, you can avoid storing it and just store the data from the response that you want to your component here. If you wanted to get something from your this.response, you're going to have to go deep into it. It would be cleaner to just pull out only what you need, and then use it in your code with just one {{myStuffIPulledOut}} vs {{response.thingIWantBefore.actualThing.}}

Why data get displayed on the html and instantly disappear? Angular

I receive an array of objects in which I have to create new properties to display in the html
public inicializeData() {
this.loaderService.eLoader.emit(true);
this.cartsSubscription = this.cartsService.getCarts().subscribe(carts => {
this.orders = carts;
this.orders.forEach(cart => {
cart.date = cart.functional_id.substring(0, 8);
cart.order_number = cart.functional_id.substring(8, 14);
});
this.load = true;
this.loaderService.eLoader.emit(false);
});
}
so that an object after the creation of the new properties
{
"functional_id": "201911291131250012400000SD4AYAA1",
"transactions": [
{
"quantity": 2,
"price": 140,
"item": {
"name": "Carton de 10 coffrets",
"description": "+ 2 recharges d'argile offertes",
"product": {
"name": "Coffret empreinte rouge"
}
}
},
{
"quantity": 1,
"price": 0,
"item": {
"name": "Petit modèle",
"description": "Par 25",
"product": {
"name": "Sacs blancs",
"description": "Pour les crémations Plurielles"
}
}
}
],
"date": "20191129",
"order_number": "113125"
},
In this function I extract from the property 'functional_id' a data formed by the first 8 digits, which correspond to the date of creation, and another formed by the following 6 digits, which corresponds to a registration number.
These are the data I keep in this function with the name of 'cart.date' and 'cart.order_number' respectively.
When I show them in the html they load immediately but, in a matter of a second, the two data I created disappear from the screen.
<div class='order-list'>
<span *ngIf="!orders.length">No orders available</span>
<div class="fade-in" *ngFor="let order of orders; let i = index;">
<div class="row ciev-row header-row d-none d-lg-flex" [ngClass]="{'last': i === orders.length - 1}" (click)="toggle(order)">
<div class="col-sm-2 my-auto">{{order.functional_id}}</div>
<div class="col-sm-2 my-auto">{{order.date | date}}</div>
<div class="col-sm-2 my-auto">{{order.order_number}}</div>
</div>
</div>
I don't understand why I can't find a solution.
Someone to give me an idea that I am doing wrong.
Recently I faced similar kind of issue.
My goal was to add the text to a list below a text html input box when user enters something into it and hits enter key.
import { Component, OnInit } from '#angular/core';
import { FormArray, FormControl, FormGroup } from '#angular/forms';
#Component({
selector: 'add-subject-form',
template: `
<form (submit)="doNotSubmitForm($event)">
<input
type="text" class="form-control"
(keyup.enter) = "addSubject(subject)" #subject>
<ul class="list-group">
<li
*ngFor="let item of items.controls"
class="list-group-item">{{item.value}}</li>
</ul>
</form>
`,
styleUrls: ['./add-subject-form.component.css']
})
export class AddSubjectFormComponent{
form = new FormGroup({
subjects: new FormArray([])
});
addSubject(pItem: HTMLInputElement) {
this.items.push(new FormControl(pItem.value));
pItem.value = '';
}
get items() {
return this.form.controls['subjects'] as FormArray;
}
// By default the form gets submitted on keyUp.enter event.
// doNotSubmitForm method prevents that default behaviour
doNotSubmitForm(event: any) {
event.preventDefault();
}
}
I could achieve the functionality using above template and necessary code in corresponding ts component class. But the problem was the text box was becoming empty and the list below it was vanishing as the html form was getting submitted by default on keyup.enter event.
Hence I had to call following method on submit event of the form.
doNotSubmitForm(event: any) {
event.preventDefault();
}
Hope this information enriches the context.
I was stuck with the fact that the data disappeared on the screen thinking that it was a problem of html or css and I didn't see why. But in reality it was a problem of the subscription that depended on a service called in other components and by the flow of the application I entered twice and it overwrote me the data, showing the originals.
Thank you all for your time and help.

Access an Array inside an Object with square brackets inside a ngFor

Basically, I've created a HTML that similar to the example :
<div *ngFor="let formElements of formTemplate">
<div *ngFor="let opt of staticEnumVals[formElements.testId]">
<!-- do smth here -->
</div>
</div>
Basically formTemplate is an Array with Objects, each Object has a property called "testId".
staticEnumVals is an Object thats build like that
{
"testId": [ {},{},{} ],
"testId2" [ {},{},{} ],
}
The keys "testId" and "testId2" are actual keys that match the Keys from formTemplate[i].testId.
Basically I want to iterate through an array out of my staticEnumVals and the array is selected dynamically based on the id from the first *ngFor
Basically im looking for an elegant way to achieve my second iteration, square brackets doesnt work.
I think my problem is clear enough, im sorry for the weird title.
Thanks in advance
After adding types to the staticEnumVals this worked for me.
#Component({
selector: 'test',
template: `
<div *ngFor="let formElements of formTemplate">
<div *ngFor="let opt of staticEnumVals[formElements.testId]">
{{opt.name}}
</div>
</div>
`
})
export class TestComponent {
private formTemplate: { testId: string }[] = [
{testId: '3'}, {testId: '2'}, {testId: '3'}
];
private staticEnumVals: { [id: string]: [any] } = {
'1': [{name: 'id1'}],
'2': [{name: 'id2'}],
'3': [{name: 'id3'}],
};
}

Show MongoDB info in HTML

I'm working on a website using a MEAN stack, and now I am trying to show some MongoDB data in my HTML pages by using Angular. But I don't seem to get it done.
This is the data in MongoDB I want to show in my HTML
{
"badkamer" : {
"block1" : {
"title" : "Badkamer",
"content" : "string"
}
}
}
This is the Angular function retrieving the data:
app.controller('cityCtrl', function($scope,$http){
$scope.specials = function(){
$scope.special = [];
$http.get('/specialdata').then(function(d){
$scope.special = d.data;
console.log(d.data);
},function(err){
console.log(err);
});
};
});
This is where I want it to show in my HTML:
<div ng-controller="cityCtrl" ng-init="specials()" ng-bind="special">
<div class="title">{{special.badkamer.block1.title}}</div>
<p>{{special.badkamer.block1.content}}</p>
</div>
</div>
When i console.log(d.data), I get this:
[Object]
0: Object
badkamer: Object
block1: Object
content: "Text",
title: "Badkamer"
But when I try it like this, the bind option shows all the data at once in my HTML. How can I get it working by using the Angular {{}} tags?
From the console.log, you can see that its an array, so you will need to use index, like this,
<div ng-controller="cityCtrl" ng-init="specials()" ng-bind="special">
<div class="title">{{special[0].badkamer.block1.title}}</div>
<p>{{special[0].badkamer.block1.content}}</p>
</div>
</div>
or change the code in controller.,
$scope.special = d.data[0];

Angular ng-repeat with nested json objects?

I have a JSON object, represented as such:
{
"orders" : [
{
"ordernum" : "PRAAA000000177800601",
"buyer" : "Donna Heywood"
"parcels" : [
{
"upid" : "UPID567890123456",
"tpid" : "TPID789456789485"
},
{
"upid" : "UPID586905486090",
"tpid" : "TPID343454645455"
}
]
},
{
"ordernum" : "ORAAA000000367567345",
"buyer" : "Melanie Daniels"
"parcels" : [
{
"upid" : "UPID456547347776",
"tpid" : "TPID645896579688"
},
{
"upid" : "UPID768577673366",
"tpid" : "TPID784574333345"
}
]
}
]
}
I need to do a repeater on the second level of this, a list of the "upid" numbers.
I know already how to get the top level
<li ng-repeat="o in orders">{{o.ordernum}}</li>
But I am unclear on the sequence to loop a level down. For example, this is wrong:
<li ng-repeat="p in orders.parcels">{{p.upid}}</li>
I also know how to nest repeaters to get this, but in this case i don't need to display the top level at all.
CLARIFICATION
The goal here is to have one list with the 4 "upid" numbers (there are 2 for each parcel, and there are 2 parcels in the order).
Actually its same answer of #sylwester. The better way to put it in filter. And you can reuse it by passing propertyName parameter.
In your case we passed parcels
JS
myApp.filter('createarray', function () {
return function (value, propertyName) {
var arrayList = [];
angular.forEach(value, function (val) {
angular.forEach(val[propertyName], function (v) {
arrayList.push(v)
});
});
return arrayList;
}
});
HTML
<ul>
<li ng-repeat="o in ordersList.orders | createarray: 'parcels'">{{o.upid}}</li>
</ul>
Here is working Fiddle
You can just create new array 'parcels' like in demo below:
var app = angular.module('app', []);
app.controller('homeCtrl', function($scope) {
$scope.data = {
"orders": [{
"ordernum": "PRAAA000000177800601",
"buyer": "Donna Heywood",
"parcels": [{
"upid": "UPID567890123456",
"tpid": "TPID789456789485"
}, {
"upid": "UPID586905486090",
"tpid": "TPID343454645455"
}]
}, {
"ordernum": "ORAAA000000367567345",
"buyer": "Melanie Daniels",
"parcels": [{
"upid": "UPID456547347776",
"tpid": "TPID645896579688"
}, {
"upid": "UPID768577673366",
"tpid": "TPID784574333345"
}]
}]
};
$scope.parcels = [];
angular.forEach($scope.data.orders, function(order) {
angular.forEach(order.parcels, function(parcel) {
$scope.parcels.push(parcel)
})
})
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="homeCtrl">
<ul>
<li ng-repeat="o in parcels">{{o.upid}}</li>
</ul>
</div>
</div>
Seems like you just need a double-nested for loop -
<ul>
<div ng-repeat="o in orders">
<li ng-repeat="p in o.parcels">{{p.upid}}</li>
</div>
</ul>
The HTML might be a little ugly here, but I'm not sure what exactly you are going for. Alternatively you could just create a new array of the parcels via mapping.
Searching a lot for nice and simple solution for iterating dynamically. I came up with this
JAVASCRIPT (angular): a person is an example of nested object. the is_object function will be use in the HTML view.
$scope.person = {
"name": "john",
"properties": {
"age": 25,
"sex": "m"
},
"salary": 1000
}
// helper method to check if a field is a nested object
$scope.is_object = function (something) {
return typeof (something) == 'object' ? true : false;
};
HTML: define a template for simple table. the 1st TD is the key which is displayed. another TD (2 or 3, but never both) will be show the value if its not an object (number / string), OR loop again if its an object.
<table border="1">
<tr ng-repeat="(k,v) in person">
<td> {{ k }} </td>
<td ng-if="is_object(v) == false"> {{ v }} </td>
<td ng-if="is_object(v)">
<table border="1">
<tr ng-repeat="(k2,v2) in v">
<td> {{ k2 }} </td>
<td> {{ v2 }} </td>
</tr>
</table>
</td>
</tr>
</table>
The reason that <li ng-repeat="p in orders.parcels">{{p.upid}}</li> does not work the way you expect is because the parcels array is an object inside each individual order in your order array, i.e. it is not an object of the orders array itself.
If your orders array is defined on the $scope of a controller, then you create the array on the $scope variable:
$scope.allParcels = $scope.orders
.map(function (elem) {
return elem.parcels;
}) // get an array where each element is an array of parcels.
.reduce(function (previousValue, currentValue) {
return previousValue.concat(currentValue);
}); // concat each array of parcels into a single array of parcels
then on the template, you can use <li ng-repeat='p in allParcels'>{{p.upid}}</li>
If, however, you do not want to place the array on the $scope, I believe you can do something similar to this:
<li ng-repeat="p in orders
.map(function (elem) {
return elem.parcels;
})
.reduce(function (previousValue, currentValue) {
return previousValue.concat(currentValue);
})">{{p.upid}}</li>
although I'm not 100% sure that Angular will evaluate the .map/.reduce in the ng-repeat expression (also having an array generated this way in an ng-repeat is ill-advised since angular would have to constantly generate a new array via map/reduce on each $digest cycle).