Vaadin 18 | Необходимо передать сообщение от клиента к серверу, используя Lit-Template и HTML-текст.
Я пытаюсь вызвать функцию на стороне сервера от клиента, используя littemplate. У меня есть одна проблема, по которой мне нужна помощь. Я добавляю HTML-текст с атрибутом на стороне сервера, который имеет настраиваемый компонент hello-world2.
Я передаю один атрибут (name="Vaadin") в этот пользовательский компонент. Я ожидаю, что когда пользователь нажимает на «helloButton», значение «Vaadin» должно быть отправлено на сервер и вызвано «acceptMessage». Но на данный момент я получаю сообщение об ошибке, как показано на прикрепленном скриншоте.
Я делаю это, потому что в моем текущем приложении я уже создал таблицу HTML. И для нескольких столбцов я пытаюсь включить и использовать HTML-тег <vaadin-button>. Поэтому, когда пользователь нажимает на эту кнопку, я ожидаю сообщения / значения на стороне сервера для дальнейшей обработки.
Пожалуйста, расскажите мне, как это сделать.
Лит-шаблон
// hello-world2.ts
import { customElement, html, LitElement, property } from "lit-element";
import "@vaadin/vaadin-button/vaadin-button";
import "@vaadin/vaadin-text-field/vaadin-text-field";
import "@vaadin/vaadin-ordered-layout/vaadin-vertical-layout";
import { showNotification } from "@vaadin/flow-frontend/a-notification";
@customElement('hello-world2')
export class HelloWorld extends LitElement {
public $server1?: HelloWorldServerInterface;
@property({ type: String })
name = '';
render() {
return html`<vaadin-vertical-layout theme="padding spacing">
<vaadin-button id="helloButton" @click="${this.sendMessage}">Message Button</vaadin-button>
</vaadin-vertical-layout>`;
}
sendMessage() {
showNotification("Hello : " + this.name); //Works well, displays the notification
this.$server1!.acceptMessage(this.name); // Issue here.
}
}
interface HelloWorldServerInterface {
greet(): void;
acceptMessage(arg0: String): void;
}
Ява
package com.example.application.views.helloworld;
import com.example.application.views.main.MainView;
import com.vaadin.flow.component.ClientCallable;
import com.vaadin.flow.component.Html;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.littemplate.LitTemplate;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.template.Id;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
@CssImport("./views/helloworld/hello-world-view.css")
@Route(value = "hello", layout = MainView.class)
@PageTitle("Hello World")
//@Tag("hello-world2") -- Commented, if I uncomment this then I can see two buttons on browser.
@JsModule("./views/littemplate/hello-world2.ts")
public class HelloWorldView extends HorizontalLayout {
public HelloWorldView() {
Html html = new Html("<hello-world2 name=\"Vaadin\"></hello-world2>");
add(html);
}
@ClientCallable
public void acceptMessage(String message) {
System.out.println("Message from client : " + message);
}
}
3 ответа
Если вы хотите в Java представить Java API для пользовательского элемента на стороне клиента, он должен расширять
Component
(или же
LitTemplate
если вы хотите использовать
@Id
аннотации) вместо, и вам следует раскомментировать аннотацию и удалить
new Html
а также
add(html)
части.
Когда у тебя есть
@Tag("hello-world2")
это означает, что, добавляя на страницу, он фактически добавляет
<hello-world2></hello-world2>
поскольку это тогда рассматривается как клиентское представление этого компонента Java. Поэтому, когда вы также вручную добавляете HTML для него в конструктор, вы получите
<hello-world2><hello-world2></hello-world2></hello-world2>
(поэтому вы увидите в два раза больше контента).
Теперь, когда у вас есть
@Tag
закомментировано здесь, и вы расширяете это означает, что
HelloWorldView
= компонент, поскольку он наследует
@Tag("vaadin-horizontal-layout")
из
HorizontalLayout
(вместо того, чтобы быть прямым Java API для вашего настраиваемого элемента) на стороне клиента. А потом
<vaadin-horizontal-layout>
экземпляр получит
$server.acceptMessage()
метод, поэтому вы не можете вызвать его из
this
in, поскольку он будет объявлен только для своего родителя.
Вот рабочий пример (я проверял, работает) того, что, как мне кажется, вы пытаетесь сделать (или что-то подобное):
hello-world2.ts
import { customElement, html, LitElement, property } from 'lit-element';
import '@vaadin/vaadin-button/vaadin-button';
import '@vaadin/vaadin-ordered-layout/vaadin-vertical-layout';
import { showNotification } from '@vaadin/flow-frontend/a-notification';
@customElement('hello-world2')
export class HelloWorld2View extends LitElement {
$server?: HelloWorldServerInterface;
@property({ type: String })
name = '';
render() {
return html`
<vaadin-vertical-layout theme="padding spacing">
<vaadin-button id="helloButton" @click="${this.sendMessage}">Message Button</vaadin-button>
</vaadin-vertical-layout>
`;
}
sendMessage() {
showNotification("Hello : " + this.name);
this.$server!.acceptMessage(this.name);
}
}
interface HelloWorldServerInterface {
greet(): void;
acceptMessage(arg0: String): void;
}
HelloWorld2View.java
:
package com.example.application.views.helloworld;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.PageTitle;
import com.example.application.views.MainLayout;
import com.vaadin.flow.component.ClientCallable;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
@Route(value = "hello", layout = MainLayout.class)
@PageTitle("Hello World")
@Tag("hello-world2")
@JsModule("./views/littemplate/hello-world2.ts")
public class HelloWorld2View extends Component {
public HelloWorld2View() {
setName("Vaadin");
}
public void setName(String name) {
getElement().setProperty("name", name);
}
@ClientCallable
public void acceptMessage(String message) {
System.out.println("Message from client : " + message);
}
}
Я не уверен, что именно здесь используется для свойства или почему вы хотите установить его в конструкторе компонента, но здесь я также добавил сеттер для него в Java API. В качестве альтернативы вы можете установить значение по умолчанию для
name
также в файле TS, а не в конструкторе Java.
Похоже, ты можешь этого хотеть
<hello-world2>
быть компонентом, который предназначен для добавления в представление (вместо того, чтобы представлять целое представление само по себе). В этом случае у вас должен быть файл TS и Java (как указано выше) специально для этого компонента, а затем использовать его в некотором представлении и
HelloWorld2View
выше, вероятно, следует назвать просто
HelloWorld2
или что-то подобное, чтобы не путать с представлением.
public class SomeOtherView extends Div {
public SomeOtherView() {
HorizontalLayout hl = new HorizontalLayout();
HelloWorld2 helloComponent = new HelloWorld2();
helloComponent.setName("Vaadin");
hl.add(helloComponent);
add(hl);
}
}
или же
public class SomeOtherView extends HorizontalLayout {
public SomeOtherView() {
HelloWorld2 helloComponent = new HelloWorld2();
helloComponent.setName("Vaadin");
add(helloComponent);
}
}
Вот еще один рабочий пример, который может быть ближе к тому, что вы хотите.
hello-world2.ts
:
import { customElement, html, LitElement, property } from 'lit-element';
import '@vaadin/vaadin-button/vaadin-button';
import '@vaadin/vaadin-ordered-layout/vaadin-vertical-layout';
import { showNotification } from '@vaadin/flow-frontend/a-notification';
@customElement('hello-world2')
export class HelloWorld2 extends LitElement {
@property({ type: String })
name = '';
render() {
return html`
<vaadin-vertical-layout theme="padding spacing">
<vaadin-button id="helloButton" @click="${this.sendMessage}">Message Button</vaadin-button>
</vaadin-vertical-layout>
`;
}
sendMessage() {
const $server = (this.parentElement as HtmlElementWithMyViewServerInterface).$server;
showNotification("Hello : " + this.name);
$server!.acceptMessage(this.name);
}
}
interface MyViewServerInterface {
acceptMessage(message: String): void;
}
interface HtmlElementWithMyViewServerInterface extends HTMLElement {
$server?: MyViewServerInterface;
}
:
@Route(value = "myview", layout = MainLayout.class)
@PageTitle("My View")
@JsModule("./views/littemplate/hello-world2.ts")
public class MyView extends HorizontalLayout {
public MyView() {
Html html = new Html("<hello-world2 name='Vaadin'></hello-world2>");
add(html);
}
@ClientCallable
public void acceptMessage(String message) {
System.out.println("Message from client : " + message);
}
}
Хотя в идеале у вас также должен быть класс компонентов Java для
<hello-world2>
и используйте это в представлении вместо
Html
составная часть. Это могло выглядеть так:
HelloWorld2.java
:
@Tag("hello-world2")
@JsModule("./views/littemplate/hello-world2.ts")
public class HelloWorld2 extends Component {
public HelloWorld2() {
setName("");
}
public HelloWorld2(String name) {
setName(name);
}
public void setName(String name) {
getElement().setProperty("name", name);
}
}
MyView.java
:
@Route(value = "myview", layout = MainLayout.class)
@PageTitle("My View")
public class MyView extends HorizontalLayout {
public MyView() {
add(new HelloWorld2("Vaadin"));
}
@ClientCallable
public void acceptMessage(String message) {
System.out.println("Message from client : " + message);
}
}
Ваадин устанавливает переменную с именем $ server, вы не можете изменить это имя. Это причина того, что у вас есть ошибка, потому что $ server1 не существует.
Вам следует переименовать $ server1 в $ server, и он должен работать.