I have been trying to display an array of posts using tailwind's Accordion. When I console log the posts, the array of posts is there but there seems to be an issue of expanding the accordion. This only happens with dynamic data, when I tested with a hardcoded array, it works fine. With dynamic data it's showing the comment of "bindings={"ng-reflect-ng-for-of": "[object Object],[object Object"}".
For creating a post and returning the array of posts, I am using a service, whenever a new post gets pushed, I will emit the new array of posts where the template will then subscribe to it. What am I doing wrong, please help a lost soul..
post-create.component.ts:
export class PostCreateComponent {
constructor(public postService: PostService){}
onSubmit(form: NgForm) {
if(form.invalid){
return;
}
this.postService.addPost(form.value.title, form.value.content);
form.reset();
}
}
post-service.ts:
@Injectable({ providedIn: 'root' })
export class PostService {
posts: Post[] = [];
updatePosts = new Subject<Post[]>()
getPosts() {
return [...this.posts];
}
returnUpdatePostListener() {
return this.updatePosts.asObservable();
}
addPost(title: string, content: string) {
const post: Post = {
title: title,
content: content
}
this.posts.push(post);
this.updatePosts.next([...this.posts]);
}
}
post-list.component.ts:
export class PostListComponent implements OnInit, OnDestroy {
constructor(public postService: PostService) { }
posts: Post[] = [];
postsSub!: Subscription;
ngOnInit(): void {
this.posts = this.postService.getPosts();
this.postsSub = this.postService.returnUpdatePostListener().subscribe(
(posts: Post[]) => this.posts = posts
)
}
ngOnDestroy(): void {
this.postsSub.unsubscribe();
}
}
post-list-component.html:
<div id="accordion-collapse" data-accordion="collapse">
<div *ngFor="let post of posts; let i = index">
<h2 attr.id="accordion-collapse-heading-{{i}}">
<button type="button" [ngClass]="i === 0 ? 'rounded-t-xl':'rounded-none'"
class="flex items-center justify-between w-full p-5 font-medium rtl:text-right text-gray-500 border border-b-0 border-gray-200 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-800 dark:border-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 gap-3"
attr.data-accordion-target="#accordion-collapse-body-{{i}}" aria-expanded="false"
attr.aria-controls="accordion-collapse-body-{{i}}">
<span>{{post.title}} </span>
<svg data-accordion-icon class="w-3 h-3 rotate-180 shrink-0" aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 5 5 1 1 5" />
</svg>
</button>
</h2>
<div attr.id="accordion-collapse-body-{{i}}" class="hidden" attr.aria-labelledby="accordion-collapse-heading-{{i}}">
<div class="p-5 border border-b-0 border-gray-200 dark:border-gray-700 dark:bg-gray-900">
<p class="mb-2 text-gray-500 dark:text-gray-400">{{post.content}}</p>
</div>
</div>
</div>
</div>
Calling initFlowbite
again after rendering the new data seems to work:
@Component(...)
export class App {
posts: Post[] = [];
ngOnInit() {
initFlowbite();
}
add() {
this.posts = this.posts.concat({
title: 'article ' + this.posts.length,
content: 'some content',
});
setTimeout(() => {
initFlowbite();
});
}
}
In this example I'm using setTimeout
so that Angular can finish rendering before feeding the new markup to Flowbite.
This solution feels a bit hackish. You may want to consider something like https://flowbite-angular.com/accordion or use [ngClass]
without Flowbite like Q.Rey suggested.