【问题标题】:How to initialize components in vaadin-dialog如何在 vaadin-dialog 中初始化组件
【发布时间】:2022-01-12 11:11:19
【问题描述】:

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

test-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;
    }

test-date-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;
    }

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

我在 JS / TS / Lit 方面经验很少,也许我做错了什么。

【问题讨论】:

    标签: vaadin lit vaadin-fusion vaadin22


    【解决方案1】:

    @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 属性绑定 (&lt;some-element .some-property="${this.someValue}"&gt;)。但是在这种情况下,如果您想使用一些自定义值来扩展日期选择器的 I18N 设置,这是不可避免的。结合对话框将其内容传输到一个单独的元素中,这使得这个用例更加复杂。

    【讨论】:

    • 谢谢,它工作得很好,也许你的答案应该在组件的文档中。
    • @user17612316 酷!我想到的另一个想法是使用自定义 Lit 指令 (lit.dev/docs/templates/custom-directives) 将自定义 I18N 值合并到日期选择器的 I18N 设置中。基本上应该可以实现一个处理这部分的指令:datePicker.i18n = {...datePicker.i18n,...i18n}。如果您有很多想要本地化日期选择器的视图,这将使其更易于维护。
    猜你喜欢
    • 1970-01-01
    • 2019-01-24
    • 2011-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-24
    相关资源
    最近更新 更多