Autodesk Forge按钮动态添加Angular绑定输入框无法输入的问题求助
Hey there! Let's break down why your dynamically added input field isn't accepting input, and walk through solutions to get your Angular bindings working with the Forge Viewer toolbar.
The Root Problem
When you use jQuery to inject raw HTML like <input [(ngModel)]="input.facade"> into the Forge button, Angular has no idea this new DOM element exists. Angular compiles templates during initialization, so any HTML added later via jQuery or vanilla JS won't have its directives (like ngModel) or event listeners processed. That's why your input shows up but doesn't respond to typing or trigger the change event.
Solution 1: Use Angular's *ngIf to Toggle Input (Simplest Approach)
Instead of dynamically inserting HTML, predefine your input in your component's template and toggle its visibility with Angular's *ngIf. This keeps the input under Angular's control so bindings work as expected.
- Add the input to your component template (e.g.,
your-component.html):
<!-- Hidden by default, will be attached to the Forge button later --> <div #facadeInputContainer class="input-wrapper" style="display: none; z-index: 100000;"> <input class="custom-input" type="text" [(ngModel)]="input.facade" (change)="onChange($event)" spellcheck="false" > </div>
- Modify your component class to toggle visibility and attach the input to the Forge button:
import { ViewChild, ElementRef } from '@angular/core'; // ... @ViewChild('facadeInputContainer', { static: false }) facadeInputContainer: ElementRef; private isInputVisible = false; public loadMobileToolbar() { /////////////////////// Facade /////////////////////// // @ts-ignore const button1 = new Autodesk.Viewing.UI.Button('mobile-text-button-facade'); const button2 = new Autodesk.Viewing.UI.Button('mobile-coloring'); button1.onClick = (event) => { this.isInputVisible = !this.isInputVisible; const buttonElement = $('#mobile-text-button-facade'); if (this.isInputVisible) { buttonElement.attr('data-before', ''); // Attach the pre-defined input container to the Forge button $(button1.container).append(this.facadeInputContainer.nativeElement); this.facadeInputContainer.nativeElement.style.display = 'block'; } else { buttonElement.attr('data-before', 'Facade'); this.facadeInputContainer.nativeElement.style.display = 'none'; } }; // ... rest of your button setup code remains the same }
Solution 2: Dynamically Create an Angular Component (For Complex Use Cases)
If you need more flexibility (e.g., reusable input components), use Angular's ComponentFactoryResolver to dynamically create and attach a component to the Forge toolbar. This ensures full Angular functionality.
- Create a reusable input component:
// custom-input.component.ts import { Component, Input, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-custom-input', template: ` <input class="custom-input" type="text" [(ngModel)]="value" (change)="onChange.emit($event)" spellcheck="false" > `, styles: [`.custom-input { z-index: 100000; }`] }) export class CustomInputComponent { @Input() value: string; @Output() onChange = new EventEmitter<Event>(); }
- Update your main component to use the dynamic component:
import { ComponentFactoryResolver, ViewContainerRef, ComponentRef } from '@angular/core'; import { CustomInputComponent } from './custom-input.component'; // ... private inputComponentRef: ComponentRef<CustomInputComponent> | null = null; constructor( private resolver: ComponentFactoryResolver, private viewContainerRef: ViewContainerRef ) {} public loadMobileToolbar() { // ... existing button setup code button1.onClick = (event) => { const buttonElement = $('#mobile-text-button-facade'); if (this.inputComponentRef) { // Clean up existing component this.inputComponentRef.destroy(); this.inputComponentRef = null; buttonElement.attr('data-before', 'Facade'); } else { buttonElement.attr('data-before', ''); // Create component factory and instance const factory = this.resolver.resolveComponentFactory(CustomInputComponent); this.inputComponentRef = this.viewContainerRef.createComponent(factory); // Bind data and events this.inputComponentRef.instance.value = this.input.facade; this.inputComponentRef.instance.onChange.subscribe((event) => { this.input.facade = (event.target as HTMLInputElement).value; this.onChange(event); }); // Attach component to Forge button $(button1.container).append(this.inputComponentRef.location.nativeElement); } }; // ... rest of your code } // Clean up component on destroy ngOnDestroy() { if (this.inputComponentRef) { this.inputComponentRef.destroy(); } }
- Register the component (for Angular versions < 9, add it to your module's
entryComponentsarray; newer versions don't require this).
Key Takeaways
- Avoid mixing jQuery DOM manipulation with Angular templates—Angular needs to manage elements to process bindings and directives.
- Use
*ngIffor simple toggle scenarios, or dynamic components for more complex, reusable UI elements.
内容的提问来源于stack exchange,提问作者dabuchera

