I have a task to develop a system that can update each employee's image in angular
form: FormGroup;
departments: FormGroup;
public fb: FormBuilder as instantiated in the constructor
this.form = this.fb.group({
companyName: [''],
companyAddress: [''],
department: this.fb.array([]),
})
createDepartment() {
this.departments = this.fb.group({
id: [''];
code: [''],
employees: this.fb.array([]),
});
}
get departmentControls() {
return (this.form.get(department) as FormArray).controls
}
export interface Department {
id: number;
code: string;
employees: Employee[];
}
export interface Employee {
id: number;
path: string;
name: string;
}
Here, imagePath is an s3 bucket URL
<div
formArrayName="department"
*ngFor="
let department of departmentControls;
let departmentIndex = index
"
>
<div [formGroupName]="departmentIndex">
// departmentIndex form group
<div
formArrayName="employee"
*ngFor="
let employee of department.get('employees').controls;
let employeeIndex = index
"
>
<div [formGroupName]="employeeIndex">
<input
type="file"
hidden
(change)="onUpdateHandler($event, department.value.id, employee.value.path)"
[id]="'department-image-input-' + employeeIndex"
/>
<img [src]="`${imagePath}${employee.value.path}?tr=w-200,h-200`" class="image-wrapper__img" />
<button
nbButton
status="warning"
(click)="onImageChangeClick(departmentIndex, employeeIndex)"
>
Change
</button>
</div>
</div>
</div>
</div>
I need to open a particular index file to update the image path
below code is tried but excepted image is not changed
onImageChangeClick(departmentIndex: number, employeeIndex: number) {
// below code needs to change
const element = document.getElementById(`department-image-input-${employeeIndex}`) as HTMLInputElement;
element.click();
}
**for example, when 0 departmentIndex's department has 1 employee and 1 departmentIndex's
the department has 1 employee when I click departmentIndex 1 employee's change button to
change image it change 0 departmentIndex's 0 employeeIndex employee image**
if departmentIndex 1 has 3 employees and departmentIndex 2 has 5 employees;
When I click departmentIndex 2 's employeeIndex 2 's image it changes departmentIndex=1 's employeeIndex 2's image
onUpdateHandler(event: Event, departmentId: number, path: string) {
const target = event.target as HTMLInputElement;
const file = target.files[0];
const data = new FormData();
data.append('file', file, file.name);
data.append('path', path);
// code for sent data to backend
}
Related
I have an typeorm entity of the following form:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName: string;
#Column()
isActive: boolean;
}
Now I am looking to get the instance fields from this entity class into an array,
like below:
['id', 'firstName', 'lastName', 'isActive']
How can I achieve that?
I tried the following but with no help:
class Me extends User{
constructor() {
super()
}
}
const me = new Me()
console.log(Object.keys(me)) // []
You might be able to do something like this:
const userProps = getConnection()
.getMetadata(User)
.ownColumns
.map(column => column.propertyName);
This means that you obtain metadata from an entity if you take the class function (AKA constructor) and use the .getMetadata method on the DB connection.
Reference:
Get all properties from a entity #1764
Give initial values in your User class to the properties. They are null now, Object.keys wont list them. Try this:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number = 0;
#Column()
firstName: string = "";
#Column()
lastName: string = "";
#Column()
isActive: boolean = false;
}
Alternative solution is when you fill the attrs manually:
const user = new User();
user.id = 0;
user.firstName = "";
user.lastName = "";
user.isActive = false;
I have a many to many relation between my products and categories entity like this:
#Entity('products')
export class productsEntity extends BaseEntity{
#PrimaryGeneratedColumn()
id: number;
//..other columns
#ManyToMany( type => categoryEntity, category => category.products, {eager: true,})
#JoinTable({name:"products_categories",
joinColumn: {name: 'productId', referencedColumnName: 'id'},
inverseJoinColumn: {name: 'categoryId', referencedColumnName:'id'}
})
categories: categoryEntity[];
}
Then i have a category entity:
#Entity('categories')
export class categoryEntity extends BaseEntity{
#PrimaryGeneratedColumn()
id: number;
#Column({nullable: false, default: 'all', unique: true})
title: string;
#ManyToMany(()=> productsEntity, product => product.categories)
products: productsEntity[];
}
I am trying to save a new product if the category exists in category Entity. by this code:
async addProduct(productDetails: productsEntity, username, categor: categoryEntity){
const {title, description, price, units} = productDetails;
try{
let newProduct = new productsEntity();
newProduct.title = title;
newProduct.description = description;
newProduct.price = price;
newProduct.units = units;
newProduct.soldBy = username;
newProduct.categories = newProduct?.categories ?? [];
newProduct.categories.push(categor);
categor.products= categor?.products?? [];
categor.products.push(newProduct);
await newProduct.save();
await categor.save();
}
catch(err){
this.logger.error(err.message);
throw new HttpException('Failed adding Product.', HttpStatus.INTERNAL_SERVER_ERROR)
}
}
Now everything seems to be fine except one problem:
productId | categoryId
-----------+------------
8 | 2
24 | 1
This gets updated instead of making a new row for a new product if i add with an existing id. like this
productId | categoryId
-----------+------------
8 | 2
25 | 1
What am i doing wrong?? I cant find any clue.
I have two entities: User and Photo with relationship OneToMany. One user can have many photos.
User:
#Entity('users')
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
firstName: string;
#Column()
lastName: string;
#Exclude()
#Column()
password: string;
#Column({ default: '' })
avatar: string;
#OneToMany(() => Photo, photo => photo.user, { cascade: true })
photos: Photo[];
}
And Photo:
#Entity('photos')
export class Photo {
#PrimaryGeneratedColumn()
id: number;
#Column()
url: string;
#Column({default: ""})
description: string;
#ManyToOne(() => User, user => user.photos)
user: User;
}
So, my question is: how can i add new photo to specific user? I tried to do something like this, but it doesn't work as expected.
#Patch(':id/photos')
addPhoto(#Param('id') id: number, #Body() createPhotoDto: CreatePhotoDto){
return this.userService.createImage(id, createPhotoDto);
}
async createImage(id: number, createPhotoDto: CreatePhotoDto) {
const user = await this.usersRepository.findOne(id);
const newUser = await this.usersRepository.preload({
id: +id,
photos: [...user.photos, createPhotoDto]
});
return this.usersRepository.save(newUser);
}
What I got from the CreatePhotoDto that you save the image and bind it with the user,
one of the easiest way to do it, First declare you imageRepository in your service then :
async createImage(id: number, createPhotoDto: CreatePhotoDto) {
const user = await this.usersRepository.findOne(id);
return await this.imageRepository.save({url:createPhotoDto.url,user:user});
// if you want to return the user with the image added you make another find with the relation exp:
// return await this.usersRepository.findOne(id,{ relations: ['photos'],});
}
There are 2 entities named Article and Classification. And the relation of them is #ManyToMany.
Here's my question: How to save the relation?
My code as below:
#Entity()
export class Article {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#CreateDateColumn()
createTime: Date;
#UpdateDateColumn()
updateTime: Date;
#Column({
type: 'text',
})
content: string;
#Column({
default: 0,
})
likeAmount: number;
#Column({
default: 0,
})
commentAmount: number;
}
#Entity()
export class Classification {
#PrimaryGeneratedColumn()
id: number;
#CreateDateColumn()
createTime: Date;
#UpdateDateColumn()
updateTime: Date;
#Column()
name: string;
#ManyToMany(type => Article)
#JoinTable()
articles: Article[];
}
I can save the Article and Classification successful. But I'm not sure how to save the relation of them.
I have tried to save the relation via below code:
async create(dto: ArticleClassificationDto): Promise<any> {
const article = this.repository.save(dto);
article.then(value => {
console.log(value);//console the object article
value.classification.forEach(item => {
const classification = new Classification();
classification.id = item.id;
classification.articles = [];
classification.articles.push(value);
this.classificationService.save(classification);
})
});
console.log(article);
return null;
}
And the post data strcture like that
{
"name":"artile name",
"content":"article content",
"classification":[{
"id":4
},{
"id":3
}]
}
At the beginning, it works.
But when I post the data again, the old record was replaced rather create another record.
What should I do next?
Just look below code please.
async create(dto: ArticleClassificationDto): Promise<any> {
this.repository.save(dto).then(article => {
article.classification.forEach(item => {
this.ClassificationRepository.findOne(
{
// the privous method is get all the articles from databse and push into this array
// relations: ['articles'],
where: { id: item }// now I change the data strcture, just contains id instead of {id}
}
).then(classification => {
// console.log(article);
console.log(classification);
// cmd will show ' UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'push' of undefined' withous below line code. But if I init the array manually,the old record will be replaced again.
// classification.articles = [];
classification.articles.push(article);
this.ClassificationRepository.save(classification);
});
})
})
return null;
}
How to save relations?
Let's assume you have an array of articles and you want to create a relation to a classification entity. You just assign the array to the property articles and save the entity; typeorm will automatically create the relation.
classification.articles = [article1, article2];
await this.classificationRepository.save(classification);
For this to work, the article entities have to be saved already. If you want typeorm to automatically save the article entities, you can set cascade to true.
#ManyToMany(type => Article, article => article.classifications, { cascade: true })
Your example
async create(dto: ArticleClassificationDto): Promise<any> {
let article = await this.repository.create(dto);
article = await this.repository.save(article);
const classifications = await this.classificationRepository.findByIds(article.classification, {relations: ['articles']});
for (const classification of classifications) {
classification.articles.push(article);
}
return this.classificationRepository.save(classifications);
}
in my case i have user and role, 1st you have to initialize your manytomany in your entities :
in user entity :
#ManyToMany((type) => Role, {
cascade: true,
})
#JoinTable({
name: "users_roles",
joinColumn: { name: "userId", referencedColumnName: "id" },
inverseJoinColumn: { name: "roleId" }
})
roles: Role[];
in role entity :
//Many-to-many relation with user
#ManyToMany((type) => User, (user) => user.roles)
users: User[];
in my service i create a new entity from my data then i added role data to my new entity object :
let entity = await this.userRepository.create(data);
let entity2 = {
...entity,
roles: data.selectedRoles,
};
const user = await this.userRepository.save(entity2);
this is the exemple in typeorm website :
const category1 = new Category();
category1.name = "animals";
await connection.manager.save(category1);
const category2 = new Category();
category2.name = "zoo";
await connection.manager.save(category2);
const question = new Question();
question.title = "dogs";
question.text = "who let the dogs out?";
question.categories = [category1, category2];
await connection.manager.save(question);
I am trying to make an interactive form that on each row lists an item together with a remove button (called verwijderen in my example). These items are retrieved from the database and each instantiated as a custom object called LaborPeriod.
These objects are then transformed into FormGroup object and then added to a FormArray. The definition of this formgroup is the following:
let formGroup = this.fb.group({
id: this.account.laborperiods[i].id,
beginDate: this.account.laborperiods[i].beginDate,
endDate: this.account.laborperiods[i].endDate,
hours: this.account.laborperiods[i].hours,
account: this.account.laborperiods[i].account
})
if(!this.laborPeriodArray){
this.laborPeriodArray = new FormArray([])
}
this.laborPeriodArray.push(formGroup)
}
The definition of the laborPeriodArray in the main FormGroup is also laborPeriodArray. The main FormGroup looks like the following:
constructor(private fb: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private accountService: AccountService) {
this.title = "Account aanpassen";
//console.log(this.account.enabled + " : " + this.account.role)
this.accountUpdateForm = this.fb.group({
name: [''],
username: ['', [Validators.required, Validators.email]],
status: '',
permission:'',
laborPeriodArray:this.fb.array([])
});
}
The entire component looks like this: Here you can see that laborPeriodArray gets initialized in the onInit method.
constructor(private fb: FormBuilder,
private route: ActivatedRoute,
private router: Router,
private accountService: AccountService) {
this.title = "Account aanpassen";
//console.log(this.account.enabled + " : " + this.account.role)
this.accountUpdateForm = this.fb.group({
name: [''],
username: ['', [Validators.required, Validators.email]],
status: '',
permission:'',
laborPeriodArray:this.fb.array([])
});
}
ngOnInit(): void {
let formGroupArray: FormGroup[] = [];
this.route.paramMap
.switchMap((params: ParamMap) =>
this.accountService.getAccount(params.get("id")))
.subscribe(account => {
this.account = account;
for(let i=0; i < account.laborperiods.length; i++){
let formGroup = this.fb.group({
id: this.account.laborperiods[i].id,
beginDate: this.account.laborperiods[i].beginDate,
endDate: this.account.laborperiods[i].endDate,
hours: this.account.laborperiods[i].hours,
account: this.account.laborperiods[i].account
})
if(!this.laborPeriodArray){
this.laborPeriodArray = new FormArray([])
}
this.laborPeriodArray.push(formGroup)
}
console.log("laborPeriod" + JSON.stringify(this.laborPeriodArray.length))
this.ngOnChanges();
});
}
ngOnChanges(): void {
if (this.account !== undefined) {
this.accountUpdateForm.reset({
name: this.account.name,
username: this.account.username,
status: this.account.enabled,
permission: this.account.admin,
laborPeriodArray: this.laborPeriodArray
});
}
}
All the FormGroup items are added but not displayed. The rows are blank. This is the relevant snippet from my HTML page
<table class="table table-hover">
<thead>
<tr>
<th>Begin datum</th>
<th>Eind datum</th>
<th>Aantal uren</th>
<th>Actie</th>
</tr>
</thead>
<tbody>
<tr formArrayName="laborPeriodArray" *ngFor = "let laborperiod of laborPeriodArray.controls; let i = index" [formGroupName]="i">
<td formControlName="beginDate">{{laborperiod.value.get('beginDate') | date:'yyyy-MM-dd'}}</td>
<td formControlName="endDate">{{laborperiod.value.get('endDate') | date:'yyyy-MM-dd'}}</td>
<td formControlName="hours">{{laborperiod.value.get('hours')}}</td>
<button type="button" class="btn btn-default">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
Verwijderen
</button>
</tr>
This is giving me the browser error ERROR Error: Cannot find control with unspecified name attribute but I am specifying the formControlName for each td row. So I don't understand what is causing the error. Furthermore I would also like to know how I can link the Remove button to the data corresponding to each row. I take it I have to use the index I for this but i'm not sure.
EDIT:
After applying AJT_82's solution I still don't have it working. It appears to have something to do with the database retrieved data itself. When I add the example account array of AJT_82 to my ngOnInit() like so:
account1 = { laborperiods: [{ id: 1, hours:11 }, { id: 2, hours:22 }, { id: 3, hours:33 }] }
ngOnInit(): void {
this.route.paramMap
.switchMap((params: ParamMap) =>
this.accountService.getAccount(params.get("id")))
.subscribe(account => {
this.account = account;
for(let i=0; i < account.laborperiods.length; i++){
let formGroup = this.fb.group({
id: this.account1.laborperiods[i].id,
beginDate: this.account.laborperiods[i].beginDate,
endDate: this.account.laborperiods[i].endDate,
hours: this.account1.laborperiods[i].hours,
account: this.account.laborperiods[i].account
})
this.laborPeriodArray.push(formGroup)
}
console.log(JSON.stringify(account.laborperiods[0].beginDate))
this.ngOnChanges();
});
}
it works but it shows only 3 rows. That's the total length of the example account array.
This is the account class:
export class Account {
id: number;
username: string;
name: string;
enabled: boolean
password: string;
person: Person;
admin: boolean;
laborperiods: LaborPeriod[]
remainingLeaveHours:number;
}
and this is the LaborPeriod class:
export class LaborPeriod{
id: number
beginDate: Date
endDate: Date
hours: number
account: Account
}
Is there anything wrong with its field declarations?
You cannot have formArrayName on the same element as your iteration and formGroupName, you need to move the formArrayName to an upper level. Also I see no use of the formControlName, as these are not editable fields, and Angular will throw error for this. Also the use of for example...
{{laborperiod.value.get('beginDate') | date:'yyyy-MM-dd'}}
is incorrect, it should be just
{{laborperiod.value.beginDate | date:'yyyy-MM-dd'}}
Assumingly in your code, the variable laberPeriodArray is declared...
this.laborPeriodArray =
this.accountUpdateForm.controls.laborPeriodArray as FormArray
since you are referring to this.laborPeriodArray in your code, therefore the following:
if(!this.laborPeriodArray){
this.laborPeriodArray = new FormArray([])
}
is redundant, you have already declared it in the build of the form as an empty FormArray. But that is just a sidenote :)
All in all, your template should look something like this:
<table formArrayName="laborPeriodArray" >
<tr *ngFor="let laborperiod of laborPeriodArray.controls;
let i = index" [formGroupName]="i">
<td>{{laborperiod.value.hours}}</td>
</tr>
</table>
DEMO
Try replacing
laborperiod.value.get('beginDate')
By
laborperiod.value['beginDate']
I also recommande that you FormGroup variable to be a Class attribute and not a OnInit() one.
I also recommande using ReactiveForms with it's API FormControls.
Let me know what happened