如何在 vaadin-dialog 中初始化组件

How to initialize components in vaadin-dialog

我正在尝试将 Vaadin 6 应用程序(使用复杂对话框 windows)重写为 Vaadin Fusion 22。初始化对话框时遇到问题。 例如在 DatePicker 中设置 i18n。 TestView.datePicker 在 firstUpdated() 上初始化,但 TestDateDialog.datePicker 始终未定义或为空。

测试-view.ts

@customElement('test-view')
export class TestView extends View {

    @state()
    private dialogOpened = false;

    @query('#date1')
    private datePicker?: DatePicker;

    @query('#test-date')
    private dialog?: TestDateDialog;

    render() {
        return html`<vaadin-vertical-layout class="w-full h-full" theme="padding">
                    <vaadin-date-picker id="date1">
                    </vaadin-date-picker>
                    <vaadin-button @click=${this.openDialog}>Open dialog</vaadin-button>
                </vaadin-vertical-layout>
      <test-date-dialog id="test-date" .dialogOpened="${this.dialogOpened}" @test_dialog_closed="${this.onDialogOpenedChanged}" ></test-date-dialog>
     `;
    }

    protected firstUpdated() {
        this.initDates();
    }

    private initDates() {
        const i18n = {
            today: '___TODAY___',
        };
        if (this.datePicker) {
            this.datePicker.i18n = {
                ...this.datePicker.i18n,
                ...i18n
            }
        }
    }

    private openDialog() {
        this.dialogOpened = true
        if(this.dialog){
            this.dialog.initDates() //1 Is this okay?
        }
    }

    onDialogOpenedChanged(e: CustomEvent) {
        this.dialogOpened = e.detail.value;
    }

测试日期-dialog.ts

@customElement('test-date-dialog')
export class TestDateDialog extends View {

    @state()
    private dialogOpened = false;

    @query('#date11')
    private datePicker?: DatePicker;

    render() {
        return html`      
      <vaadin-dialog id="testD" no-close-on-outside-click no-close-on-esc  .opened=${this.dialogOpened}
         @opened-changed="${(e: CustomEvent) => this.openDialog(e.detail.value)}"        
        .renderer="${guard([], () => (root: HTMLElement) => {
            render(html`<vaadin-vertical-layout>
                 <vaadin-horizontal-layout class="w-full">                   
                    <vaadin-button theme="icon tertiary contrast" @click="${() => this.cancel()}">
                    <vaadin-icon slot="prefix" icon="vaadin:close-big"></vaadin-icon></vaadin-button>
                </vaadin-horizontal-layout>
               <vaadin-date-picker id="date11">
                                </vaadin-date-picker>
              </vaadin-vertical-layout>
            `, root);
        })}"></vaadin-dialog>    
     `;
    }

    private openDialog(open: boolean) {
        this.dialogOpened = open;
        //if (open) {
            //this.initDates(); //2   Is this okay?
        //}
    }

    //protected firstUpdated() {
    //    this.initDates(); 
    //}

    initDates() {
        console.log(this.shadowRoot?.querySelector('#date11')) //undefined
        console.log(this.querySelector('#date11')) //null
        console.log(this.datePicker)  //null

        const i18n = {
            today: '___TODAY___'
        };
        if (this.datePicker) {
            this.datePicker.i18n = {
                ...this.datePicker.i18n,
                ...i18n
            }
        }
    }

    cancel() {
        const options = {
            detail: {
                opened: false
            },
            bubbles: true,
            composed: true
        };
        this.dispatchEvent(new CustomEvent<CancelButtonEvent>('test_dialog_closed', options));

        this.dialogOpened = false;
    }

如何在打开对话框后进行一些初始化工作(使用异步后端调用),不仅使用@属性,还通过@query 使用组件?

本人对JS/TS/Lit经验不多,可能是我做的不对

@query 装饰器无法处理由渲染器函数生成的对话框内容,因为所有对话框内容都被传输到一个单独的覆盖元素中,该元素不是您的 test-date-dialog 的子元素.例如,您可以通过检查浏览器开发工具中的对话框内容来确认这一点。 @query 装饰器在这种情况下不起作用,因为它只查找应用它的元素的子元素。

这里的一个解决方案可能是将对话框内容从渲染器函数中提取到一个单独的 Lit 组件(例如 TestDateDialogContent),然后在渲染器函数中渲染该组件。在 TestDateDialogContent 中,您可以使用 @query 装饰器访问日期选择器。您还可以使用常规生命周期方法(例如 firstUpdated)进行其他初始化,例如在日期选择器上设置 I18N 设置,或对后端进行异步调用。

test-date-dialog-content.ts中:

@customElement('test-date-dialog-content')
class TestDialogContent extends LitElement {
  @query('#date11')
  private datePicker!: DatePicker;

  protected firstUpdated() {
    this.initDates();
  }

  private initDates() {
    const i18n = {
      today: '___TODAY___'
    };
    this.datePicker.i18n = {
      ...this.datePicker.i18n,
      ...i18n
    };
  }

  render() {
    return html`
      <vaadin-vertical-layout>
        ...
        <vaadin-date-picker id="date11"></vaadin-date-picker>
        ...
      </vaadin-vertical-layout>
    `;
  }
}

test-date-dialog.ts中:

<vaadin-dialog
  .renderer="${guard([], () => (root: HTMLElement) => render(html` <test-date-dialog-content></test-date-dialog-content>`, root))}"
>
</vaadin-dialog>

理想情况下,您希望尽可能避免强制访问日期选择器等单个元素,而是使用 Lit 属性 绑定 (<some-element .some-property="${this.someValue}">)。但是,在这种情况下,您想要使用一些自定义值扩展日期选择器的 I18N 设置,这是不可避免的。结合将其内容传输到单独元素的对话框,这使得这个用例有点复杂。