How to make a button come out of another button - html

I'm using Angular 5, and I wanted to press another button to get another button.
I do not know if I would have to use the ngIf, but I do not know how to identify the previous button.
example.ts
import { Component, OnInit } from '#angular/core';
import { Hero } from '../hero';
import { HeroService } from '../hero.service';
#Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
heroes: Hero[];
constructor(private heroService: HeroService) { }
ngOnInit() {
this.getHeroes();
}
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
add(name: string): void {
name = name.trim();
if (!name) { return; }
this.heroService.addHero({ name } as Hero)
.subscribe(hero => {
this.heroes.push(hero);
});
}
delete(hero: Hero): void {
this.heroes = this.heroes.filter(h => h !== hero);
this.heroService.deleteHero(hero).subscribe();
}
}
example.html
<ul class="heroes">
<li *ngFor="let hero of heroes">
<a routerLink="/detail/{{hero.id}}">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</a>
<button class="delete" title="delete hero" (click)="delete(hero)">x</button>
</li>
</ul>

In your component.ts:
#Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
heroes: Hero[];
showSecondButton: boolean = false;
constructor(private heroService: HeroService) { }
ngOnInit() {
this.getHeroes();
}
delete(hero: Hero): void {
this.heroes = this.heroes.filter(h => h !== hero);
this.heroService.deleteHero(hero).subscribe();
this.showSecondButton = true;
}
}
In your html:
<ul class="heroes">
<li *ngFor="let hero of heroes">
<a routerLink="/detail/{{hero.id}}">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</a>
<button class="delete" title="delete hero" (click)="delete(hero)">x</button>
<button *ngIf="showSecondButton"> Your second button</button>
</li>
</ul>

so your best bet is to create two buttons within a div, then have a toggle that switches the button that is displayed.
Example:
<div>
<button *ngIf=!displaysecondbutton (click)='showsecondbutton()'>First Button Yo</button>
<button *ngIf=displaysecondbutton>SecondButtonYo</button>
</div>
typescript file;
displaysecondbutton = false;
showsecondbutton(){
this.displaysecondbutton = true;
}

To keep the code concise I would implement an if statement in your delete function:
public deleteConfirmed = false;
public confirmDelete = false;
delete(hero: Hero): void {
if (deleteConfirmed) {
confirmDelete = deleteConfirmed = false;
this.heroes = this.heroes.filter(h => h !== hero);
this.heroService.deleteHero(hero).subscribe();
}
else{
confirmDelete = true;
}
}
Then in your html:
<button class="delete" title="delete hero" (click)="delete(hero)">Delete</button>
<button *ngIf="confirmDelete" class="delete" title="delete hero" (click)="deleteConfirmed=true; delete(hero)">Confirm Deletion</button>
This way, you are always cycling the same function and just altering some states along the way.

Related

How add product to cart with Angular and Firestore

I manage to fetch the product data through the URL and when I am on the product details page there is a button that allows me to add the product to a sub-collection of the logged in user.
Below is what I want:
users/{userID}/shopping/{productID}
details-product.component.html displaying information in product details
<div class="uk-container panier" *ngIf="product">
<div id="modal-center" class="uk-flex-top" uk-modal>
<div class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical">
<button class="uk-modal-close-default" type="button" uk-close></button>
<img [src]="(product | async)?.imageURL" class="nav-left">
</div>
</div>
<div class="uk-child-width-1-2#s" uk-grid>
<div>
<a href="#modal-center" uk-toggle>
<img [src]="(product | async)?.imageURL" class="nav-left" alt="">
</a>
</div>
<div>
<div class="uk-dark uk-padding">
<h3> {{ (product | async)?.name }}</h3>
<hr class="uk-divider-icon">
<div class="uk-flex prix">
<p class="uk-text">
<span>{{ (product | async)?.price | currency: 'XOF' }} </span>
</p>
</div>
<hr class="uk-divider-icon">
<button class="uk-button uk-button-default uk-form-width-medium" (click)="showDialog()">
<i class='bx bxs-cart-add'></i>
J'achète le produit
</button>
</div>
</div>
</div>
</div>
shopping-cart.service.ts
import { AngularFirestore, AngularFirestoreCollection } from '#angular/fire/compat/firestore';
import "firebase/auth";
import { User } from 'src/app/models/user.model';
import { Product } from 'src/app/models/product.model';
#Injectable({
providedIn: 'root'
})
export class ShoppingCardService {
userCollection: AngularFirestoreCollection<User>;
productsCollection: AngularFirestoreCollection<Product>;
constructor(private readonly dbstore: AngularFirestore) {
this.userCollection = this.dbstore.collection('users');
this.productsCollection = this.dbstore.collection('products');
}
addToMyCart(product: Product, userID: string, qteProduct: number) {
const userDoc = this.dbstore.firestore.collection('users').doc(userID);
const productDoc = this.dbstore.firestore.collection('products').doc(product.id);
const userShoppingProduct = userDoc.collection('shopping');
return userShoppingProduct.doc(product.id).set(product);
}
}
product.service.ts
import { Injectable } from '#angular/core';
import { AngularFirestore, AngularFirestoreCollection } from '#angular/fire/compat/firestore';
import { Router } from '#angular/router';
import { Observable } from 'rxjs';
import { Product } from 'src/app/models/product.model';
#Injectable({
providedIn: 'root'
})
export class ProductsService {
productCollection: AngularFirestoreCollection<Product>;
constructor(private dbstore: AngularFirestore, private router: Router) {
this.productCollection = this.dbstore.collection('products', (ref) =>
ref.orderBy('category', 'desc')
);
}
getDetailProduct(productId: string): Observable<any> {
return this.productCollection.doc(productId).valueChanges();
}
}
details-product.component.ts
import { ActivatedRoute } from '#angular/router';
import { Observable } from 'rxjs/internal/Observable';
import { User } from 'src/app/models/user.model';
import { Product } from 'src/app/models/product.model';
import { ProductsService } from '../../shared/services/products.service';
import { ShoppingCardService } from 'src/app/shared/services/shopping-card.service';
import { AngularFirestoreCollection } from '#angular/fire/compat/firestore';
import { AddToCartComponent } from './add-to-cart.component'
import { MatDialog } from '#angular/material/dialog';
#Component({
selector: 'app-details-product',
templateUrl: './details-product.component.html',
styleUrls: ['./details-product.component.css']
})
export class DetailsProductComponent implements OnInit {
quantity: number = 0;
productIdRoute: string;
isMyProduct: boolean = false;
product: Observable<Product>;
userCollection!: AngularFirestoreCollection<User>
constructor(
private route: ActivatedRoute,
private productService: ProductsService,
private shoppingCardService: ShoppingCardService,
private titleService: Title,
private dialog: MatDialog,
) {
const routeParams = this.route.snapshot.paramMap;
this.productIdRoute = String(routeParams.get('productId'));
this.product = this.productService.getDetailProduct(this.productIdRoute);
}
showDialog(): void {
const data = this.dialog.open(AddToCartComponent, {
width: '30rem',
data: { productID: this.productIdRoute },
});
console.log(data);
}
add-to-cart.component.ts
component in modal to confirm adding to cart
import { Component, OnInit, Inject } from '#angular/core';
import { MatDialog, MAT_DIALOG_DATA } from '#angular/material/dialog';
import { ShoppingCardService } from '../../shared/services/shopping-card.service';
import { Product } from 'src/app/models/product.model';
#Component({
selector: 'app-add-to-cart',
template: `
<div class="relative p-4 w-full max-w-md h-full md:h-auto">
<div class="p-6 text-center">
<svg aria-hidden="true" class="mx-auto mb-4 w-14 h-14 text-gray-400 dark:text-gray-200" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<h3 class="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">Do you want to add this product?</h3>
<button (click)="onAddPubpik(data.product, data.userID)" type="button" class="text-white bg-gray-800 hover:bg-gray-800 focus:ring-4 focus:outline-none font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center mr-2">
Yes, i add to cart
</button>
</div>
</div>
`,
styleUrls: ['./details-product.component.css']
})
export class AddToCartComponent implements OnInit {
constructor(
#Inject(MAT_DIALOG_DATA) public data: { product: Product, userID: string, qteProduct: number },
private dialog: MatDialog,
private shoppingService: ShoppingCardService
) {}
ngOnInit(): void {
}
async onAddPubpik(product: Product, userID: string): Promise<void> {
this.dialog.closeAll();
const qteProduct = (product.quantity += 1);
product.isMyProduct = true;
await this.shoppingService.addToMyCart(product, userID, qteProduct);
}
}
When i confirm, in console :
On this line const qteProduct = (product.quantity += 1);, product is undefined. You’re not passing product to the matdialog component in your showDialog() method hence the error.

Angular Adding class to just right added comment, not all of them

I need to add bootstrap effect fadeInDown to a newly added comment in my comments section.
It works but unfortunately it affects all of the comments when I reload the page and the effect is also added when I edit a comment.
Is it possible to add this specific class only to newly created comment? not affecting the rest of comments nor the other application functionalities?
I will be glad for any advice
This is my comment section:
Comment section
This is the structure of my files: Files structure
These are the components I'm working on:
Comment section HTML
<h6>Comments: ({{commentsCount}})</h6>
<app-new-comment class="d-block mt-3" [requestId]="requestId" (update)="updateComments()">
</app-new-comment>
<app-comment *ngFor="let comment of comments; let i = first"
[ngClass]="{'fadeInDown': i}"
class="d-block mt-3 animated"
[comment]="comment"
(update)="updateComments()"></app-comment>
Comment section TS
export class CommentSectionComponent implements OnInit, OnDestroy {
#Input() requestId: string
comments: CommentDetailsDto[]
commentsCount = 0
private subscription: Subscription
constructor(
private commentsService: CommentsService,
private alert: AlertService
) {
}
ngOnInit(): void {
this.updateComments()
}
ngOnDestroy(): void {
this.unsubscribe()
}
updateComments(): void {
this.unsubscribe()
this.subscription = this.commentsService.getComments(this.requestId)
.subscribe({
next: (comments: CommentDetailsDto[]) => {
this.comments = comments.reverse()
this.commentsCount = this.getCommentsCount(comments)
},
error: (error: HttpErrorResponse) => {
this.alert.handleHttpError(error)
}
})
}
private getCommentsCount(comments: CommentDetailsDto[]): number {
return comments.reduce(
(cnt, comment) => cnt + 1 + (comment.replies ? this.getCommentsCount(comment.replies) : 0),
0
)
}
private unsubscribe(): void {
if (this.subscription instanceof Subscription) {
this.subscription.unsubscribe()
}
}
}
Comment section SCSS
#keyframes fadeInDown {
from {
opacity: 0;
transform: translate3d(0, -100%, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
.fadeInDown {
animation-name: fadeInDown;
}
New comment component HTML
<div class="d-flex">
<app-user-image class="mr-3"></app-user-image>
<textarea #textArea class="d-block" placeholder="Add comment" [(ngModel)]="newComment">
</textarea>
</div>
<div class="text-right mt-3">
<button class="btn btn-primary font-weight-bold px-5" type="button"
[disabled]="!newComment" (click)="submit()">
Post
</button>
</div>
New comment TS
export class NewCommentComponent implements OnInit {
#ViewChild('textArea') textArea: ElementRef<HTMLTextAreaElement>
#Input() requestId?: string
#Input() comment?: CommentDetailsDto
#Output() update = new EventEmitter()
newComment: string
private readonly commentResponseObserver = {
error: (error: HttpErrorResponse) => {
this.alert.handleHttpError(error)
},
complete: () => {
delete this.newComment
this.update.emit()
this.alert.showSuccess('Comment submitted successfully')
}
}
constructor(
private commentsService: CommentsService,
private alert: AlertService,
private route: ActivatedRoute
) {
}
ngOnInit(): void {
if (this.comment) {
this.textArea.nativeElement.focus()
}
}
submit(): void {
if (this.requestId) {
this.addComment()
} else if (this.comment) {
this.addReply()
}
}
addComment(): void {
if (this.newComment) {
this.commentsService.addComment(this.requestId, this.newComment)
.subscribe(this.commentResponseObserver)
}
}
addReply(): void {
if (this.newComment) {
this.commentsService.addReply(this.route.snapshot.queryParamMap.get('requestId'), this.comment, this.newComment)
.subscribe(this.commentResponseObserver)
}
}
}
Comment component HTML
<div class="comment">
<app-user-image></app-user-image>
<div class="position-relative d-inline-block flex-fill">
<div class="d-flex justify-content-between align-items-center">
<div><strong>{{comment.author.name}} / {{comment.author.id}}</strong><span
class="date">{{comment.created | krakenDateTime}}</span></div>
<div class="actions">
<button *ngIf="this.hateoas.supports(comment, 'update') && !edit"
type="button" class="bg-transparent border-0" title="Edit"
(click)="toggleEdit()"><i class="icon-kraken icon-kraken-edit"></i></button>
<button *ngIf="this.hateoas.supports(comment, 'delete')"
type="button" class="bg-transparent border-0" title="Delete"
(click)="displayDeletionConfirmation()"><i class="icon-kraken icon-kraken-trash"></i></button>
</div>
</div>
<textarea *ngIf="edit; else readonlyComment"
#textarea
class="d-block w-100"
style="min-height: 7rem;"
[rows]="rows()"
[(ngModel)]="commentContent"></textarea>
<ng-template #readonlyComment>
<div [innerHTML]="commentContentHtml()"></div>
</ng-template>
<strong *ngIf="showReplyButton"
class="reply-button"
(click)="toggleReplyComponent()">Reply</strong>
</div>
</div>
<div *ngIf="edit" class="animated fadeIn text-right mt-3">
<button class="btn btn-sm discard" (click)="cancelEdit()">Discard</button>
<button class="btn btn-sm ml-2 update" (click)="updateComment()">Update</button>
</div>
<div class="replies">
<app-new-comment *ngIf="showNewReplyWindow"
class="d-block mt-3"
[comment]="comment"
(update)="this.update.emit()"></app-new-comment>
<app-comment *ngIf="firstReply"
class="d-block my-3"
[comment]="firstReply"
(update)="update.emit()"></app-comment>
<div *ngIf="moreReplies.length" class="replies-toggle" (click)="showMoreReplies =
!showMoreReplies">
<div class="horizontal-bar"></div>
<span class="mx-3">{{showMoreReplies ? 'Hide' : 'See'}} {{moreReplies.length}} more
comments</span>
<div class="horizontal-bar"></div>
</div>
<div *ngIf="showMoreReplies">
<app-comment *ngFor="let reply of moreReplies"
class="d-block my-3"
[comment]="reply"
(update)="update.emit()"></app-comment>
</div>
<span id="temporaryLastRow"></span>
Comment TS
export class CommentComponent implements OnInit {
#ViewChild('textarea', {static: true}) textarea: ElementRef<HTMLTextAreaElement>
#Input() comment: CommentDetailsDto
#Output() update = new EventEmitter()
edit = false
showReplyButton = false
showNewReplyWindow = false
showMoreReplies = false
commentContent: string
firstReply: CommentDetailsDto
moreReplies: CommentDetailsDto[]
constructor(
private commentsService: CommentsService,
private modalFactoryService: ModalFactoryService,
public hateoas: HateoasService,
private alert: AlertService
) {
}
ngOnInit(): void {
this.commentContent = this.comment.content
this.showReplyButton = this.hateoas.supports(this.comment, 'reply')
this.moreReplies = this.comment.replies.reverse().slice(1)
this.firstReply = this.comment.replies[0]
}
toggleEdit(): void {
this.edit = !this.edit
}
updateComment(): void {
this.commentsService.updateComment(this.comment, this.commentContent)
.subscribe({
error: (error: HttpErrorResponse) => {
this.alert.handleHttpError(error)
},
complete: () => {
this.alert.showSuccess('Comment updated successfully')
this.update.emit()
}
})
}
cancelEdit(): void {
this.edit = false
this.commentContent = this.comment.content
}
displayDeletionConfirmation(): void {
this.modalFactoryService.openConfirmationModal(
'Delete Comment',
{
message: 'Are you sure you want to delete this comment?',
yesOptionButton: 'Yes',
noOptionButton: 'No'
},
() => {
this.hateoas.execute(this.comment, 'delete')
.subscribe({
error: (error: HttpErrorResponse) => {
this.alert.handleHttpError(error)
},
complete: () => {
this.alert.showSuccess('Comment deleted successfully')
this.update.emit()
}
})
}
)
}
toggleReplyComponent(): void {
this.showNewReplyWindow = !this.showNewReplyWindow
}
commentContentHtml(): string {
return this.commentContent.replace(/\n/g, '<br>')
}
rows(): number {
const newLines = this.commentContent.match(/\n/g) || []
return newLines.length + 1
}
}
export interface Author {
id: string
name: string
}
export interface CommentDetailsDto extends Resource {
content: string
author: Author
replies: CommentDetailsDto[]
created: string
}

Disable scrolling in typescript as soon as property is true

I have a hamburger inside of my header - as soon as it is clicked, I wan't to disable scroll - since the hamburger is moving on scroll.
I tried to set the hamburger to position: fixed. But this one changed the position of its neighbour, which looked weird.
Is there a way to realize this in typescript - so that as soon clicked is true, scrolling is disabled. Or is there a better approach?
https://stackblitz.com/edit/angular-beiajz
export class HeaderComponent implements OnInit {
clicked = false;
onClick(): void {
this.clicked = !this.clicked;
}
constructor() { }
ngOnInit(): void {
}
}
<div class="container-fluid mobile position-absolute">
<div class="row m-0 w-100">
<div class="col-6 offset-3 justify-content-center d-flex">
<a class="align-self-center" routerLink="">
<h1>NØREBRO STUDIOS</h1>
</a>
</div>
<div class="col-3">
<button class="hamburger hamburger--collapse" (click)="onClick()" [class.is-active]="clicked" type="button">
<span class="hamburger-box">
<span class="hamburger-inner"></span>
</span>
</button>
</div>
<div class="container-fluid position-fixed min-vh-100 px-0 slide-in" [class.get-active]="clicked">
</div>
</div>
</div>
One of the way is passing a event from the child to parent whenever click is made on the hamburger menu and changing the parent class CSS
in app.component.html
<app-test (hamburgerClick)="hamburgerClickEvent($event)"></app-test>
<div [ngClass]="{
'container-fluid-overlay': overlayFlag === true,
'container-fluid': overlayFlag === false }">
</div>
in app.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
overlayFlag = false; // overlay flag..onload overlay is hidden
public hamburgerClickEvent(e:boolean):void{
this.overlayFlag= e;
}
}
in app.component.css
.container-fluid {
min-height: 200vh;
}
.container-fluid-overlay {
height: auto;
}
in test.component.ts
import { Component, OnInit , Output, EventEmitter} from '#angular/core';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {
#Output() hamburgerClick = new EventEmitter();//emitter
clicked = false;
onClick(): void {
this.clicked = !this.clicked;
this.hamburgerClick.emit(this.clicked); //emitting clicked event to parent
}
constructor() { }
ngOnInit(): void {
}
}
Hope it helps :)

multi page registration form in angular 6

I have a parent component as under and 4 child components for registration. The data is not visible if i retrun from step L2 to L1.
Any idea?
<div class="container-fluid page-content" mat-dialog-content>
<div class="row">
<ul class="nav nav-pills nav-justified">
<li [ngClass]="{'active': this.actL1}"><a (click)="onClick('l1')" data-toggle="pill">L1</a></li>
<li [ngClass]="{'active': this.actL2}"><a (click)="onClick('l2')" data-toggle="pill">L2</a>
</li>
<li [ngClass]="{'active': this.actL3}"><a (click)="onClick('l3')" data-toggle="pill">L3</a>
</li>
<li [ngClass]="{'active': this.actL4}"><a (click)="onClick('l4')" data-toggle="pill">L4</a></li>
</ul>
<app-request-ltype *ngIf=actL1></app-request-ltype>
<app-request-ptype *ngIf=actL2></app-request-ptype>
<app-request-stype *ngIf=actL3></app-request-stype>
<app-request-rType *ngIf=actL4></app-request-rtype>
</div>
</div>
ltype component (RequestDetails ts file contains eg name:string)
import { Component, OnInit, getDebugNode } from '#angular/core';
import { FormGroup, FormBuilder } from '#angular/forms';
import { Router, ActivatedRoute } from '#angular/router';
import { RequestDetails } from '';
#Component({
selector: 'app-request-l1',
templateUrl: './request-l1.component.html',
styleUrls: ['./request-l1.component.css']
})
export class LtypeComponent implements OnInit {
requestL1Form: FormGroup;
constructor(private l1FormB: FormBuilder, private router: Router, private route: ActivatedRoute) {
this.requestL1Form = this.createL1FormGroup(l1FormB);
}
ngOnInit() {
}
createL1FormGroup(l1FB: FormBuilder) {
return l1FB.group({
l1Data: l1FB.group( new RequestDetails())
});
}
onSubmit() {
console.log('submitted values:', this.requestL1Form.value);
}
}

Angular2 dynamically added elements

I'm trying to use the dynamic component provided by #GünterZöchbauer here Angular 2 dynamic tabs with user-click chosen components to create rows and columns. So far I got successfully the rows added but still can't get the columns created inside the rows.
Here is my code:
DesignerModule
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { MaterializeModule } from '../../shared/materialize/materialize.module';
import { DesignerComponent } from './designer.component';
import { RowComponent } from './designer.component';
import { ColumnComponent } from './designer.component';
import { DynWrapperComponent } from './dyn-wrapper.component';
#NgModule({
imports: [
CommonModule,
MaterializeModule,
],
declarations: [
DesignerComponent,
DynWrapperComponent,
RowComponent,
ColumnComponent,
],
entryComponents: [
RowComponent,
ColumnComponent,
],
providers: [
]
})
export class DesignerModule {}
DynWrapperComponent
import { Component, Compiler, ViewContainerRef, ViewChild, Input, ElementRef,
ComponentRef, ComponentFactory, ComponentFactoryResolver} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
// Helper component to add dynamic components
#Component({
moduleId: module.id,
selector: 'dcl-wrapper',
template: `<div #target></div>`
})
export class DynWrapperComponent {
#ViewChild('target', {read: ViewContainerRef}) target: any;
#Input() type: any;
cmpRef:ComponentRef<any>;
private isViewInitialized:boolean = false;
constructor(private componentFactoryResolver: ComponentFactoryResolver,
private compiler: Compiler,
private el: ElementRef) {}
updateComponent() {
if(!this.isViewInitialized) {
return;
}
if(this.cmpRef) {
this.cmpRef.destroy();
}
let factory = this.componentFactoryResolver.resolveComponentFactory(this.type);
//this.resolver.resolveComponent(this.type).then((factory:ComponentFactory<any>) => {
this.cmpRef = this.target.createComponent(factory)
}
ngOnChanges() {
this.updateComponent();
}
ngAfterViewInit() {
this.isViewInitialized = true;
this.updateComponent();
}
ngOnDestroy() {
if(this.cmpRef) {
this.cmpRef.destroy();
}
}
}
DesignerComponent
import { Component, ViewChild, ElementRef, ContentChildren } from '#angular/core';
#Component({
moduleId: module.id,
selector: 'my-row',
templateUrl: 'row.component.html',
styles: [
`.row:hover {
border: 3px dashed #880e4f ;
}
`
]
})
export class RowComponent {
colIndex: number = 0;
colList: Object[] = [];
addColumn() {
this.colList.splice(this.colIndex, 0, ColumnComponent);
this.colIndex++;
}
removeColumn(colIdx: number) {
this.colList.splice(colIdx, 1);
}
}
#Component({
moduleId: module.id,
selector: 'my-column',
templateUrl: 'column.component.html',
styles: [
`.col:hover {
border: 3px solid #304ffe;
}
`
]
})
export class ColumnComponent {
}
#Component({
moduleId: module.id,
selector: 'my-designer',
templateUrl: 'designer.component.html',
})
export class DesignerComponent {
#ViewChild('builder') builder:ElementRef;
elementIndex: number = 0;
list: Object[] = [];
ngAfterViewInit() {
}
addRow() {
this.list.splice(this.elementIndex, 0, RowComponent);
this.elementIndex++;
}
remove(idx: number) {
this.list.splice(idx, 1);
}
}
DesignerComponent.html
<div #builder class="row">
<div class="s1 teal lighten-2">
<p class="flow-text">teste do html builder</p>
<div *ngFor="let row of list; let idx = index" >
<p class="flow-text">Linha {{idx}}</p>
<dcl-wrapper [type]="row"></dcl-wrapper>
<a class="btn-floating btn-small waves-effect waves-light purple" (click)="remove(idx)"><i class="material-icons">remove</i></a>
</div>
</div>
</div>
<a class="btn-floating btn-large waves-effect waves-light red" (click)="addRow()"><i class="material-icons">add</i></a>
RowComponent.html
<div #row class="row">
<div class="s12 teal lighten-2">
<p class="flow-text">adicionando linha no html builder</p>
</div>
<div *ngFor="let col of colList; let colIndex = index">
<p>Column</p>
<dcl-wrapper [type]="col"></dcl-wrapper>
</div>
<a class="btn-floating btn-small waves-effect waves-light waves-teal" (click)="addColumn()"><i class="material-icons">view_column</i></a>
</div>
ColumnComponent.html
<div class="col s1 purple lighten-2">
<p class="flow-text">column added ....</p>
</div>
This approach is generating the following error:
Expression has changed after it was checked. Previous value:
'CD_INIT_VALUE'. Current value:
Did it anyone get this working as nested elements?
Thanks very much for the help!
Try to use ngAfterContentInit hook instead of ngAfterViewInit in your DynWrapperComponent:
dyn-wrapper.component.ts
ngAfterContentInit() {
this.isViewInitialized = true;
this.updateComponent();
}