I am trying to call server side function from client using littemplate. I have checked examples on Vaadin site and found that client may call server side via this.$server._some_method.
I tried to use $server in littemplate but during frontend compilation vaadin throws error stating that "Property '$server' does not exist on type 'HelloWorld'."
Please let me know what is wrong with this program and guide me.
Thank you.
Littemplate
import {LitElement, html} from 'lit-element';
import '#vaadin/vaadin-button/vaadin-button.js';
class HelloWorld extends LitElement {
render() {
return html`
<div>
<vaadin-button id="helloButton">Click me!</vaadin-button>
</div>`;
}
sayHello(){
showNotification("Hello");
this.$server.greet(); //Problematic statement.
}
}
customElements.define('hello-world', HelloWorld);
Java
package com.example.application.littemplate;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.littemplate.LitTemplate;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.template.Id;
import com.vaadin.flow.component.textfield.TextField;
//HelloWorld.java
#Tag("hello-world")
#JsModule("./views/littemplate/hello-world.ts")
public class HelloWorld extends LitTemplate {
#Id
// Uses the vaadin-button id "helloButton"
private Button helloButton;
public HelloWorld() {
helloButton.addClickListener(event -> Notification.show("Hello " + nameField.getValue()));
}
#ClientCallable
public void greet() {
System.out.println("Hello server");
}
}
Typescript does not know that LitTemplate has a $server variable. You have to define it yourself.
You can use type any or define your interface.
For example:
private $server?: MyTestComponentServerInterface;
And add the #ClientCallable functions:
interface MyTestComponentServerInterface {
greet(): void;
}
In your case, your typescript could be:
import {LitElement, html} from 'lit-element';
import '#vaadin/vaadin-button/vaadin-button.js';
class HelloWorld extends LitElement {
private $server?: HelloWorldServerInterface;
render() {
return html`
<div>
<vaadin-button id="helloButton">Click me!</vaadin-button>
</div>`;
}
sayHello(){
showNotification("Hello");
this.$server!.greet(); // should work with autocompletion
}
}
interface HelloWorldServerInterface {
greet(): void;
}
customElements.define('hello-world', HelloWorld);
Related
I am trying to integrate KoliBri web-components (https://github.com/public-ui/kolibri) in a Vaadin project. I followed the documentation for web components integration (https://vaadin.com/docs/latest/create-ui/web-components) but I was not successful.
I want to integrate a KoliBri-button (kol-button) and therefor created getter and setter methods for the required properties of the button. When loading the website, the kol-button-component is loaded successfully from the .js file.
enter image description here
But the kol-button element in the DOM is empty and won´t show up:
enter image description here
Here is my KolButton.java:
package com.example.application.views.helloworld;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Synchronize;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
#Tag("kol-button")
#NpmPackage(value = "#public-ui/components", version = "1.1.10")
#JsModule("#public-ui/components/dist/components/kol-button")
public class KolButton extends Component {
public boolean getLabel() {
return getElement().getProperty("_label", false);
}
public void setLabel(String label) {
getElement().setProperty("_label", label);
}
public void setVariant(String variant) {
getElement().setProperty("_variant", variant);
}
public boolean getVariant() {
return getElement().getProperty("_variant", false);
}
}
And the view.java:
package com.example.application.views.helloworld;
import com.example.application.views.MainLayout;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouteAlias;
#PageTitle("Hello World")
#Route(value = "hello", layout = MainLayout.class)
#RouteAlias(value = "", layout = MainLayout.class)
public class HelloWorldView extends HorizontalLayout {
public HelloWorldView() {
var kolButton = new KolButton();
kolButton.setLabel("TestText");
kolButton.setVariant("danger");
setVerticalComponentAlignment(Alignment.END, kolButton);
add(kolButton);
}
}
Do you have any idea to solve this? Thanks in advance
I can't figure out how to pass my app object to my TypeGrapQL resolvers.
I created my types and resolvers and setup a graphql server using express-graphql. I was able to run the graph, but no luck in passing the app object to use the registered services.
My graphql.service.ts looks like this:
import { ServiceAddons } from '#feathersjs/feathers'
import { graphqlHTTP } from 'express-graphql'
import 'reflect-metadata'
import { buildSchemaSync } from 'type-graphql'
import { Container } from 'typedi'
import { Application } from '../../declarations'
import { Graphql } from './graphql.class'
import { ArticleResolver } from './resolvers/article.resolver'
// Add this service to the service type index
declare module '../../declarations' {
interface ServiceTypes {
graphql: Graphql & ServiceAddons<any>
}
}
export default async function (app: Application): Promise<void> {
const schema = buildSchemaSync({
resolvers: [__dirname + '/resolvers/*.resolver.ts'],
container: Container,
})
app.use(
'/graphql',
graphqlHTTP({
schema: schema,
graphiql: true,
})
)
}
and here's one of my resolver classes article.resolver.ts
import { Arg, Query, Resolver } from 'type-graphql'
import { Service } from 'typedi'
import { Application } from '../../../declarations'
import { Category } from '../types/category.type'
#Service()
#Resolver(Category)
export class CategoryResolver {
constructor(private readonly app: Application) {}
#Query((returns) => [Category])
async categories() {
try {
const result = await this.app.service('category').find()
return (result as any).data // TODO: Refactor to return result with pagination details
} catch (err) {
console.log('Categories resolver error', err)
return []
}
}
}
I can't do this.app.service() as this.app is undefined
Im a little confused on how dependency injection works in TypeGrapQL, any help is appreciated.
Thanks
I managed to make it work, here's my solution if anyone has the same problem:
I created a Graphql class decorated with #Service from typedi that takes in an app object as such
import { Service } from 'typedi'
import { Application } from '../../declarations'
#Service()
export class Graphql {
app: Application
//eslint-disable-next-line #typescript-eslint/no-unused-vars
constructor(app: Application) {
this.app = app
}
}
In my graphql.service.ts I initiated the class and passed down the instance to the typedi container
import { buildSchemaSync } from 'type-graphql'
import { Container } from 'typedi'
import { Application } from '../../declarations'
import { Graphql } from './graphql.class'
export default async function (app: Application): Promise<void> {
const graphql = new Graphql(app)
Container.set('graphql', graphql)
const schema = buildSchemaSync({
resolvers: [__dirname + '/resolvers/category.resolver.ts'],
container: Container, // Pass the container to the resolvers
})
// Initialize our express graphql server
}
And Finally in my resolvers I am decorating the resolver with #Service and injecting the graphql instance to the constructor:
import { Application } from '../../../declarations'
import { Graphql } from '../graphql.class'
import { Inject, Service } from 'typedi'
#Service()
#Resolver(Category)
export class CategoryResolver {
app: Application
constructor(#Inject('graphql') private readonly graphql: Graphql) {
this.app = this.graphql.app
}
// Queries and Mutations
}
This solved it to me, hope it comes with any help to you 😊
return Mono.just(key) //
.map(Service1::doSomething) //
.map(Service2:: doSomething) //
.map(Service3::getBytes);
My code is as above, I have many logs in the methods of services (Service1, Service2 and Service3), so I can confirm that those methods are not called, till I call subscribe method by manually verifying the log files. Are there any testing tools which can help me automate that testing?
Mokito can track invocations using mocks/spys. Here's an example using Spring:
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.junit4.SpringRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
#RunWith(SpringRunner.class)
public class ExampleTests {
#SpyBean
private Service1 service1;
#SpyBean
private Service2 service2;
#SpyBean
private Service3 service3;
#Test
public void verifyNothingHappensWithoutSubscribe() {
Mono.just("key")
.map(service1::doSomething)
.map(service2::doSomething)
.map(service3::getBytes);
verify(service1, times(0)).doSomething("key");
verify(service2, times(0)).doSomething("key");
verify(service3, times(0)).getBytes("key");
}
#Test
public void verifyCallsWhenSubscribed() {
Mono<byte[]> testMono = Mono.just("key")
.map(service1::doSomething)
.map(service2::doSomething)
.map(service3::getBytes);
StepVerifier.create(testMono)
.expectNextCount(1)
.verifyComplete();
verify(service1, times(1)).doSomething("key");
verify(service2, times(1)).doSomething("key");
verify(service3, times(1)).getBytes("key");
}
}
I'm trying to make a global click event directive. But document:click does not work for me.
import 'package:angular/angular.dart';
#Directive(
selector: '[clickOutside]'
)
class ClickOutsideDirective {
#HostListener('click', [r'$event.target'])
void onClick(targetElement){
print('Target:' + targetElement.toString());
}
}
When changing document:click to click I get expected behavior. But of course not globally. What am I doing wrong?
The document: and similar event scopes were removed in Dart.
Use instead
import 'dart:html';
class ClickOutsideDirective implements OnInit, OnDestroy {
StreamSubscription _docClickSub;
ngOnInit() {
_docClickSub = document.onClick.listen((event) {
print('Target:' + event.target.toString());
});
}
ngOnDestroy() {
_docClickSub?.cancel();
_docClickSub = null;
}
}
Following
https://christiandietrich.wordpress.com/2011/10/15/xtext-calling-the-generator-from-a-context-menu/
and using EclipseResourceFileSystemAccess2 instead of EclipseResourceFileSystemAccess when the line
final EclipseResourceFileSystemAccess2 fsa = fileAccessProvider.get();
give an exception. The only information I have is
// Compiled from InvocationTargetException.java (version 1.8 : 52.0, super bit)
public class java.lang.reflect.InvocationTargetException extends java.lang.ReflectiveOperationException {
I don't know how to get the stack trace in Eclipse.
does the code in the blog still function in the most recent release of Xtext?
Update 1
Snippets from plugin.xml
Handler:
<extension
point="org.eclipse.ui.handlers">
<handler
class="tuks.mcrl2.dsl.ui.handlers.Mcrl22Lps"
commandId="tuks.mcrl2.dsl.ui.commands.mcrl2lps">
</handler>
</extension>
Commands:
<extension
point="org.eclipse.ui.commands">
<command
categoryId="tuks.mcrl2.dsl.ui.category.processalgebra"
defaultHandler="tuks.mcrl2.dsl.ui.handlers.Mcrl22Lps"
description="Conver a mclr2 file to lps"
id="tuks.mcrl2.dsl.ui.commands.mcrl2lps"
name="mcrl22lps">
</command>
<category
id="tuks.mcrl2.dsl.ui.category.processalgebra"
name="Process Algebra">
</category>
</extension>
it basically works, if you do the update from EclipseResourceFileSystemAccess and Stuff and (maybe) IGenerator.
I assume in your case you dont set the Accesses ProgressMonitor and other props.
package org.xtext.example.mydsl.ui.handler;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.xtext.builder.EclipseResourceFileSystemAccess2;
import org.eclipse.xtext.generator.GeneratorContext;
import org.eclipse.xtext.generator.IGenerator2;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.ui.resource.IResourceSetProvider;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class GenerationHandler extends AbstractHandler implements IHandler {
#Inject
private IGenerator2 generator;
#Inject
private Provider<EclipseResourceFileSystemAccess2> fileAccessProvider;
#Inject
IResourceDescriptions resourceDescriptions;
#Inject
IResourceSetProvider resourceSetProvider;
#Override
public Object execute(ExecutionEvent event) throws ExecutionException {
ISelection selection = HandlerUtil.getCurrentSelection(event);
if (selection instanceof IStructuredSelection) {
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
Object firstElement = structuredSelection.getFirstElement();
if (firstElement instanceof IFile) {
IFile file = (IFile) firstElement;
IProject project = file.getProject();
IFolder srcGenFolder = project.getFolder("src-gen");
if (!srcGenFolder.exists()) {
try {
srcGenFolder.create(true, true,
new NullProgressMonitor());
} catch (CoreException e) {
return null;
}
}
final EclipseResourceFileSystemAccess2 fsa = fileAccessProvider.get();
fsa.setProject(project);
fsa.setOutputPath("src-gen");
fsa.setMonitor(new NullProgressMonitor());
URI uri = URI.createPlatformResourceURI(file.getFullPath().toString(), true);
ResourceSet rs = resourceSetProvider.get(project);
Resource r = rs.getResource(uri, true);
generator.doGenerate(r, fsa, new GeneratorContext());
}
}
return null;
}
#Override
public boolean isEnabled() {
return true;
}
}
and make sure you register the handler properly.
the
class="org.xtext.example.mydsl.ui.MyDslExecutableExtensionFactory:org.xtext.example.mydsl.ui.handler.GenerationHandler"
is crucial, especially that it consists of 2 parts, the ExtensionFactory followed by a : followed by the actual class name