How to display other component in one other *ngFor - html

I have this table in html
<form [formGroup]="myform" (ngSubmit)="submit()" >
<tbody>
<tr class="group" *ngFor="let item of products;">
<td>
<div>
{{item.product_type_id}}
</div>
</td>
<td>{{item.product_id}}</td>
</tr>
</tbody>
<form>
I have this products in ts code: this.products = this.ps.getProduct(); that getall my product.
Product have this property
export class Products {
product_id: number;
product_type_id: number;
prodcttype: ProductType[];}
When I create a product, I get my productType from ws, like this
this.pts.getAllProductType().subscribe(
producttype => {
this.producttype = producttype;
}
);
Product Type have this property
export class ProductType {
product_type_name: string;
description: number;
default_price: number;
product_type_id: string;}
I want to display in html in this {{item.product_type_id}} --> {{item.product_type_name}}
At the moment this doesn't function because product_type_name
not found in Products
ts code:
this.myform= new FormGroup({
'invoice_number': new FormControl('', [Validators.required, Validators.nullValidator]),
'invoice_date': new FormControl('', Validators.required),
'client_id': new FormControl('', Validators.required),
'products': this.fb.array([])
});
ngOnInit() {
this.products = this.ps.getProduct();
console.log(this.products)
this.pts.getAllProductType().subscribe(
producttype => {
this.producttype = producttype;
}
);
submit() {}
}
service producttype
public getAllProductType(): Observable<ProductType[]> {
let headers = new Headers();
headers.append('x-access-token', this.auth.getCurrentUser().token);
return this.http.get(Api.getUrl(Api.URLS.getAllProductType), {
headers: headers
})
.map((response: Response) => {
let res = response.json();
if (res.StatusCode === 1) {
this.auth.logout();
} else {
return res.StatusDescription.map(producttype => {
return new ProductType(producttype);
});
}
});
}
service product
private products: Products[] = [];
getProduct() {
return this.products;
}
Can you suggest me any solution, how to solve it?

From the detail question in the comments I would suggest you to make a new function in the component which would filter productType and provide you with productName. Code for same is as follow:
Add following function in componnent
getProductName(productTypeId: string) {
const [filteredProdType] = this.producttype.filter(pt => pt.product_type_id== productTypeId);
return filteredProdType.product_type_name;
}
And change your template to
<form [formGroup]="myform" (ngSubmit)="submit()" >
<tbody>
<tr class="group" *ngFor="let item of products;">
<td>
<div>
{{item.product_type_id}}
</div>
</td>
<td>{{getProductName(item.product_type_id)}}</td>
</tr>
</tbody>
<form>
this will fetch the product name of item by using product_type_id.
Hope this help. Add comments if further help needed.

Related

Calling function from another component and editing a variable values

I have a form with some input fields.
When I submit the form I want to add a row to my table dataSource with the new data.
I have a component for the form that looks like that:
FORM HTML
<form (submit)="submitForm($event)">
<app-form-element align="center" *ngFor="let el of fields| keyobject" [value]="el.value.value" [type]="el.value.type">
</app-form-element>
<button>Save User</button>
</form>
FORM TS
#Component({
selector: 'app-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.css'],
})
export class FormComponent implements OnInit {
fields!: object;
constructor() { }
ngOnInit(): void {
this.newForm();
}
newForm() {
this.fields = [{ value: "Name", type: "text" },
{ value: "Surname", type: "text" },
{ value: "Email", type: "email" }];
}
tbc = new TableComponent;
submitForm(event: any) {
let newUser = new User();
newUser.name = event.target.Name.value;
newUser.surname = event.target.Surname.value;
newUser.email = event.target.Email.value;
this.tbc.addValue(newUser);
event.preventDefault();
}
}
export class User {
name!: string;
surname!: string;
email!: string;
}
TABLE HTML
<table *ngIf="show">
<tr>
<th *ngFor="let column of headers">
{{column}}
</th>
<th>Commands</th>
</tr>
<tr *ngFor="let row of dataSource | keyobject; let i = index">
<td *ngFor="let col of headers">
{{row.value[col]}}
</td>
<td>
<button class="btn btn-default" type="button" (click)="deleteValue(i)">Delete</button>
</tr>
</table>
TABLE TS
export class TableComponent implements OnInit {
headers = ['name', 'surname', 'email'];
dataSource: any = [
{ id: 1, name: "test", surname: 'test', email: "test#gmail.com"},
];
ngOnInit(): void {
}
addValue(user: User) {
let id = this.dataSource.length + 1;
this.dataSource = [...this.dataSource, { id: id, name: user.name, surname: user.surname, email: user.email, save: false }];
this.reload();
}
deleteValue(id: any) {
this.dataSource.splice(id, 1);
this.reload();
}
public show = true;
reload() {
this.show = false;
setTimeout(() => this.show = true);
}
}
When I call the addValue function in the Form.ts it works but the dataSource doesn't get updated.
Debugging the code everything works and it looks like the record is being added to the dataSource but the table dataSource doesn't actually have the new record so it doesn't get displayed.
Notice that my deleteValue is working fine and is deleting the row from the dable and from the dataSource
I'm new to angular so any help is appreciated
I think the problem is that the changes on dataSource array from your child component are not automatically detected on push. You can force the change detection using detectChanges from ChangeDetectorRef :
#Component({
...
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TableComponent implements OnInit {
contructor(
private cdr: ChangeDetectorRef
) {}
dataSource: any = [
{ id: 1, name: "test", surname: 'test', email: "test#gmail.com"},
];
addValue(user: User) {
let id = this.dataSource.length + 1;
this.dataSource.push({ id: id, name: user.name, surname: user.surname, email: user.email});
this.reload();
}
public show = true;
reload() {
// here you can force the change detection
this.cdr.detectChanges();
...
}
}
Even though, the other solution of the changeDetector might work, it's not the best approach to tell angular to refresh. It's better to instead, just put your code in a way that angular will notice that needs to change.
I believe that in angular, a push into the array, it's not detected as a new change.
I think instead of doing the push, you could do the following:
this.dataSource = [...this.dataSource, { id: id, name: user.name, surname: user.surname, email: user.email}]
Basically, you would create a new array that contains the old array + the new data.
Anyways, to be sure this is a correct answer, could you provide the actual code you have, not a small cut of it, with both TS and HTML Templates ?

How to get a value from an Object that comes from a ManyToOne relationship in Angular CLI?

I'm using Spring Boot, Angular CLI and mySQL.
I have an Employee than can have one Marital Status, and one M Status can be in N Employees.
In localHost:8080 I get the right array json:
[{"id":1,"firstName":"Name","lastName":"surname1","emailAddress":"test#test1.com","status":{"statusId":1,"nameStatus":"Single"}
In my angular table(localHost:4200), instead I get every data but in Status column I get "[object Object]".
In have a service for each one.
When I do a registration I have a dropDown w/ all status so I get them.
This is my HTML table:
<table class="table table-bordered">
<thead>
<tr>
<th *ngFor="let col of columns">{{col}}
</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let employee of employees | paginate: {itemsPerPage: pageSize,
currentPage: page,
totalItems: employees.length} | filterAll: searchString : field">
<td *ngFor="let col of columns">{{employee[col]}}</td>
<td>
<button [ngClass]="getClassCondition(act.actionType)" *ngFor="let act of actions"
(click)="actionFunc(act, employee)">{{act.label}}</button>
</td>
</tr>
</tbody>
</table>
Here I have an ngFor that gets All employees.
Now I share also my services and my componets.ts:
status.service.ts:
#Injectable({
providedIn: 'root'
})
export class StatusService {
private baseUrl = 'http://localhost:8080/api';
constructor(
private http: HttpClient) { }
getStatus(): Observable<Status[]> {
return this.http.get<Status[]>(`${this.baseUrl}` + '/status');
}
}
employee.service.ts
const httpOptions = {
headers: new HttpHeaders({'Content-Type': 'application/json'})
};
#Injectable({
providedIn: 'root'
})
export class EmployeeService {
columns = COLUMNS;
actions = ACTIONS;
private baseUrl = 'http://localhost:8080/api/employees';
constructor(private http: HttpClient) {
}
getColumns() {
return this.columns;
}
getActions() {
return this.actions;
}
getMetadata() {
return this.metadata;
}
/** GET Employees from the server */
getEmployees(): Observable<Employee[]> {
return this.http.get<Employee[]>(this.baseUrl);
}
getEmployee(id: number): Observable<Employee> {
const url = this.baseUrl + '/' + id;
return this.http.get<Employee>(url);
}
/** PUT: update the employee on the server */
updateEmployee(employee: Employee): Observable<any> {
return this.http.put(`${this.baseUrl}/${employee.id}`, employee, httpOptions);
}
deleteEmployee(id: number): Observable<any> {
return this.http.delete(`${this.baseUrl}/${id}`, {responseType: 'text'});
}
}
Here I have also a const w/ COLUMNS name.
employee.ts
export const COLUMNS = ['id', 'firstName', 'lastName', 'emailAddress', 'status'];
export class Employee {
id: number;
firstName: string;
lastName: string;
emailAddress: string;
status: string;
}
status.ts
export class Status {
statusId: number;
nameStatus: string;
}
What do I have to do to get my status.Name?
Is there something specific?
If you need more documentation ask me.
I suspect that col return status and employee[col] return {"statusId":1,"nameStatus":"Single"}.
So the error seems correct.
You can do a workaround here if you just want to display the nameStatus :
<td *ngFor="let col of columns">{{employee[col]?.nameStatus ? employee[col]?.nameStatus : employee[col]}}</td>
You have to be more specific, how you display the data or map the data to the structure you want
1) instead of <td *ngFor="let col of columns">{{employee[col]}}</td>
you can do
<td>{{employee.firstName}}</td>
<td>{{employee.lastName}}</td>
...
<td>{{employee.status.nameStatus}}</td>
2) before binding the data, map it to the structure you want (in your controller/*.ts file).
Take a look at Array map( for that.
Your code doesn't seem to work because the controller never assigns the employee method

How to create an Array of actions and implement them in my Angular component?

I have to modify the following code with an implementation of an Array of actions (bottom page).
I saw lots of websites by I wasn't able to find something than can be used for my code.
I will have to change my html , my tableService, my component.ts and oviously my actionConfiguration.
At the moment this is my HTML:
<div class="container">
<table class="table">
<tr>
<th *ngFor="let col of columns" (click)="sortTable(col)">{{col}}</th>
<th>Actions</th>
</tr>
<tr *ngFor="let user of users | paginate: {itemsPerPage: 5,
currentPage: page,
totalItems: users.length } ; let i = index">
<td *ngFor="let col of columns">{{user[col]}}</td>
<td>
<button [ngClass]="getClassCondition(act)" *ngFor="let act of actions" (click)="actionFunc(act,i)">{{act}}</button>
</td>
</tr>
</table>
</div>
<div>
<pagination-controls (pageChange)="page = $event"></pagination-controls>
</div>
This is my component.ts:
#Component({
selector: 'app-dynamic-table',
templateUrl: './dynamic-table.component.html',
styleUrls: ['./dynamic-table.component.css']
})
export class DynamicTableComponent implements OnInit {
#Input()
users = [];
#Input()
columns: string[];
#Input()
actions: string[];
#Input()
class;
direction = false;
page: any;
constructor() {
}
sortTable(param) {
/*done*/
}
actionFunc(i, index) {
if (i === 'deleteUser') {
if (confirm('Are you sure you want to delete this item?') === true) {
this.users.splice(index, 1);
}
}
if (i === 'editUser') {
/*...*/
}
}
getClassCondition(act) {
return act === 'deleteUser' ? this.class = 'btn btn-danger' : 'btn btn-primary' ;
}
ngOnInit(): void {
}
}
This is my tableService.ts
import { USERS } from './mock-data';
#Injectable()
export class TableService {
constructor() { }
static getUsers(): Observable<any[]> {
return Observable.of(USERS).delay(100);
}
static getColumns(): string[] {
return ['id', 'firstName', 'lastName', 'age'];
}
static getActions(): string[] {
return ['deleteUser', 'editUser'];
}
}
Here's the new Task, I have to create an Array of Actions so I will be able to use it in different components but I have no idea how to do it.
I have to start from something like this, it's just an example (not complete because I don't know what to insert exactly):
actionConfig.ts
export const ACTIONS = [
{
label: 'Remove',
actionType: 'deleteUser',
},
{
label: 'Edit',
actionType: 'editUser',
},
];
A sample of Enum and a table to show data on iterating on them:
StackBlitz
You also might want to read typescript-enums-explained
Basically, the TypeScript enums are compiled to something as shown below for reverse lookup. Thats why I have added the foreach loop in constructor and created another list.
export enum Fruits {
APPLE = 'Apple',
MANGO = 'Mango',
BANANA = 'Banana',
}
is compiled to
var Fruit;
(function (Fruit) {
Fruit[Fruit["APPLE"] = 'Apple'] = "APPLE";
Fruit[Fruit["MANGO"] = 'Mango'] = "MANGO";
Fruit[Fruit["BANANA"] = 'Banana'] = "BANANA";
})(Fruit || (Fruit = {}));
UPDATE
HTML
<button [ngClass]="getClassCondition(act.actionType)" *ngFor="let act of actions"
(click)="actionFunc(act, user)">{{act.label}}</button>
COMPONENTS.TS
actionFunc(action, element: any) {
if (action.actionType === 'DELETE') {
if (confirm('Are you sure you want to delete this item?') === true) {
/*...*/
}
}
if (action.actionType === 'GO_TO') {
/*...*/
}
}
actionsConfig.ts
export const ACTIONS = [
{
label: 'Delete',
actionType: 'DELETE',
deleteApi: 'api/USERS'
},
{
label: 'Edit',
actionType: 'GO_TO',
getUrl: row => '/detail/' + row.id,
},
];

Object actions in vue

I have the following structure in Vue.
The App.vue
export default {
name : "app",
router,
data() {
return {
items: {books:[], authors:[]}
};
},
created: function() {
customServiceInstance.makeAjaxCall("books.json", "get").then(res => {
this.items.books = res.books;
return res;
})
customServiceInstance.makeAjaxCall("authors.json", "get").then(res => {
this.items.authors = res.authors;
return res;
})
customServiceInstance.makeAjaxCall("genres.json", "get").then(res => {
this.items.genres = res.genres;
return res;
})
},
methods: {
removeEntry:function(index) {
this.$delete(this.items.books, index);
customServiceInstance.makeAjaxCall('books.json', 'POST', JSON.stringify(this.items.books));
}
},
computed: {
booksWithAuthor () {
let { books, authors } = this.items
return books.map(book => ({
...book,
author: authors.find(author => author.id === book.author),
}))
},
}
}
</script>
<template>
<div id="app">
<router-link to="/home" >Home 1</router-link>
<router-link to="/home/2"> Home 2</router-link>
<router-view class="view" foo="123"></router-view>
<table class="booksTable">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>Genre</th>
<th>Image</th>
<th>Availability</th>
<th>Options</th>
</tr>
</thead>
<tbody>
<tr v-for="(book,index) in booksWithAuthor" v-bind:key="book.name">
<td>{{book.name}}</td>
<td>{{book.author.name}}</td>
<td>{{book.genre}}</td>
<td><img class="imageBook" :src="book.imageUrl"></td>
<td v-if="book.availability">Available</td>
<td v-else>Unavailable</td>
<td>
<button class="btn add">Add</button>
<button class="btn edit" >Edit</button>
<button class="btn delete" v-on:click="removeEntry(index)">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import './styling.scss';
import customService from './components/customService';
const customServiceInstance= new customService();
import Vue from 'vue';
import VueRouter from 'vue-router';
import HomeR from './components/home.vue';
import Copil from './components/copil.vue';
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{path: '/home', component: HomeR},
{path: '/home/:years', component: Copil, props:true }
]
})
And this JS
export default class CustomService {
listJson(url){
var storageLocalData = localStorage.getItem(url);
var obj=JSON.parse(storageLocalData);
console.log(obj);
};
makeAjaxCall(url, methodType, data){
this.listJson(url);
var promiseObj = new Promise(function(resolve, reject){
var storageLocalData = localStorage.getItem(url);
if(!storageLocalData){
var xhr = new XMLHttpRequest();
xhr.open(methodType, url, true);
if (data) {
xhr.send(data);
} else {
xhr.send();
}
xhr.onreadystatechange = function(){
if (xhr.readyState === 4){
if (xhr.status === 200){
var response = xhr.responseText;
var respJson = JSON.parse(response);
localStorage.setItem(url, JSON.stringify(respJson));
resolve(respJson);
} else {
reject(xhr.status);
}
}
}
}
else {
resolve(JSON.parse(storageLocalData));
}
});
return promiseObj;
};
}
I want to create an object Book and have a function getBookById(id, list),
The list being the books.json that's being loaded.I want this function to return the book object, who has name, author, genre and so on.
I tried a lot of things, but with no result.
Even tried in a ts file something like this:
export default class Book {
name: String;
id: Number;
author: String;
genre: Number;
imageUrl: String;
availability: boolean;
methods: {
getBookById:(id: Number,url: String) => Book {
}
}
Please help me
I want to create an object Book and have a function getBookById(id,
list), The list being the books.json that's being loaded.I want this
function to return the book object, who has name, author, genre
this can be achieved by the es6 array function find().
all you have to do inside your function, is:
getBookById(bookId,booksList){
return booksList.find(book=>
book.id===bookId)
}
the function will return the first array item that matches the condition (book.id===bookId), or undefined if none of them did match.

How to populate form html with json data from ws, using Typescript

I don't understand how to populate form when I get all data form ws.
I want to populate form from my function productGetAll()
my product.service
public productGetAll(): Observable<Product[]> {
let headers = new Headers();
headers.append('x-access-token', this.auth.getCurrentUser().token);
return this.http.get(Api.getUrl(Api.URLS.productGetAll), {
headers: headers
})
.map((response: Response) => {
let res = response.json();
if (res.StatusCode === 1) {
this.auth.logout();
} else {
return res.StatusDescription.map(prod => {
return new Product(prod);
});
}
});
}
This function productGetAll(){} I call in component product.ts like this code:
public prod: Product[];
this.ws.productGetAll().subscribe(
prod=> {
this.prod= prod;
}
);
This code is form that I want to populate
this.productForm = this.fb.group({
'active': new FormControl('', Validators.required),
'name': new FormControl('', Validators.required)
});
My html code:
<form [formGroup]="productForm ">
<mat-slide-toggle formControlName="active" id="active" (change)="onChange($event)" [(ngModel)]="devicee[i]" (click)="onActiveHomeboxP(item.homeboxpackage_id)">
</mat-slide-toggle>{{device}}
<div class="row">
<div class="input-field col s12">
<input formControlName="name" id="name" type="text">
</div>
</div>
</form>