Double-Click event not fired on IOS / Safari using Vaadin Grid - ios

I am creating a responsive web app using vaadin 23.1.0 and got a problem that the double click event is not fired on an Iphone with Safari. Never had any issues with that on other platforms.
The following code illustrates the problem. Button click works on IOS, doubleclick doesn't.
#PageTitle("IosTestView")
#Route(value = "IosTestView", layout = MainLayout.class)
#PermitAll
#Log4j2
public class IosTestView extends VerticalLayout {
public IosTestView() {
H2 h2 = new H2("IOS Test View");
add(h2);
List<String> testData = Arrays.asList("One", "Two", "Three", "Four", "Five");
Grid<String> testGrid = new Grid<>();
testGrid.addColumn(new ComponentRenderer<>(a -> new Button("Go", b -> show(a))));
testGrid.addColumn(a -> a);
testGrid.addItemDoubleClickListener(a -> show(a.getItem()));
testGrid.setItems(testData);
add(testGrid);
}
private void show(String msg) {
Notification notification = new Notification();
notification.setPosition(Notification.Position.MIDDLE);
notification.setText("Item : " + msg);
notification.setDuration(5000);
notification.open();
}
}
No idea if there is an easy fix for this... but adding an extra button instead of using double click is not my favorite option...
Context Menu does work on IOS but I have no possibility to get the selected item - it's always null... seems to be the same issue...
Any help really welcome :-)
Regards
Stefan

Sounds like the same root cause as the other issue you described. The other issue was fixed in 23.1.1 - https://github.com/vaadin/flow-components/issues/3296.
Please update to the current version to see if this fixes your problem as well.

Related

Using Threads to Update UI in Vaadin 14

I created a thread (via a lambda expression) to fetch some data based on user input fields but when I try to click on dropdown menus while it is retrieving data I get the mini progress bar indicator. So is a new thread even being created? What am I doing wrong here?
Button doComputation = new Button("Get Results);
doComputation.addClickListener(event -> {
UI ui = UI.getCurrent();
new Thread(() -> {
// Do some work
ui.access(() -> layout.add(results);
}).start();
});
UPDATE: RESOLVED! Unneccesary ui.access calls were made, which locked up resources. Thank you to all that commented and helped.
The code looks correct to me (apart from the missing end parenthesis after ui.access). Is that the only ui.access call, and is that all you do inside it?
I made this example for reference, and the combo box stays responsive while the background task is running.
#Route
public class ThreadView extends VerticalLayout {
public ThreadView() {
Button runThreadButton = new Button("Start thread", e -> {
UI ui = UI.getCurrent();
new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
ui.access(() -> Notification.show("Completed"));
}).start();
});
ComboBox<String> comboBox = new ComboBox<>("Items", "One", "Two", "Three");
add(runThreadButton, comboBox);
}
}
So is a new thread even being created?
You can verify that by adding a System.out.println("Thread started") and checking whether that message is printed, or by running your application in debug mode and setting a breakpoint on the part that you're interested in.
What am I doing wrong here?
The code that you showed looks fine. I would guess there's a problem outside the shown code, namely that you have forgotten that you also need to add the #Push annotation. Without that annotation, the server will have no way of directly sending messages to the client.

Nativescript IPhone X - How to "hide" home indicator behind two swipes to go to home screen

I am using Nativescript Angular, (NS version 4.1) I am trying to implement a requirement for users to swipe up twice to go home on
new IOS devices. Many mobile games have this functionality.
I know this has to do with prefersHomeIndicatorAutoHidden and
preferredScreenEdgesDeferringSystemGestures in the ViewController
Is there a way I can access these methods in Nativescript Angular?
Or just set a home indicator to require two swipes to go home?
Any help would be greatly appreciated thanks!
There is an open feature request to allow iOS root view controller properties to be overridden. You have to actually override the preferredScreenEdgesDeferringSystemGestures to UIRectEdge.All and as per Apple documentation you have to update setneedsupdateofhomeindicator as well.
But if you try to access the these properties directly (e.g. this.page.ios.prefersHomeIndicatorAutoHidden = true), it will give you error TypeError: Attempted to assign to readonly property.
These is workaround discussed here where to you have copy the controller, modify the property and assign it back to owner.
const UIViewControllerImpl = new page.Page().ios.constructor as typeof UIViewController;
const MyCustumUIViewController = UIViewController['extend'](Object.assign(
{},
// merge in the original methods
...UIViewControllerImpl.prototype,
// add additional instance method / property overrides here, such as ...
{
preferredScreenEdgesDeferringSystemGestures() {
console.log("This will be called from native!");
return UIRectEdge.All;
}
}
));
const performNavigation = frame.Frame.prototype['performNavigation'];
frame.Frame.prototype['performNavigation'] = function(navigationContext:{entry:frame.BackstackEntry}) {
const page = navigationContext.entry.resolvedPage;
const controller = (<typeof UIViewController>MyCustumUIViewController).new();
controller['_owner'] = new WeakRef(page);
controller.automaticallyAdjustsScrollViewInsets = false;
controller.view.backgroundColor = new color.Color("white").ios;
page['_ios'] = controller;
page.setNativeView(controller.view);
performNavigation.call(this, navigationContext);
}

How to solve activity not found exception when navigate to other view?

I am getting a Android.Content.ActivityNotFoundException: when I want to go to another activity.
Also I have to recreate my emulator every time I want to see my app because it causes always adb error the second time I start the emulator.
I read that this can be caused by another exception, but I checked a other code and I didnt find anything
Button btnentrar = FindViewById<Button>(Resource.Id.createlist);
btnentrar.Click += delegate
{
StartActivity(typeof(listeditorclass));
};
//activity:
private List<string> mItems;
private ListView mListView;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.listeditor);
mListView = FindViewById<ListView>(Resource.Id.listView);
mItems = new List<string>();
mItems.Add("Milch");
mItems.Add("Brot");
mItems.Add("Apfel");
MyListViewAdapter adapter = new MyListViewAdapter(this, mItems);
mListView.Adapter = adapter;}
also I dont know the diffrents between AppCompatActivity and a normal activity. So general the user should see when he clicks on the button the new view (createlist) with my list.
I hadn't used anonymous delegates on Xamarin before, that's a cool thing. Try using an intent and passing the Context:
btnentrar.Click += delegate
{
StartActivity(new Intent(this, typeof(listeditorclass)));
};

How to open custom dialog box / popup using Xamarin.Forms?

I am newbie to Xamarin.Forms and stuck with a situation where I want to open up a popup box with my control details [e.g. View Employee Details] on click of parent page.
How can I open custom dialog box / popup using Xamarin.Forms?
Any example code will be appreciated?
Thanks in advance!
If you still want to have your popup's code in its own Page you can set up some custom renderers along the following logic.
1. A ModalPage & corresponding renderer
public class ModalPage : ContentPage { }
public class ModalPageRenderer : PageRenderer {
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
this.View.BackgroundColor = UIColor.Clear;
this.ModalPresentationStyle = UIModalPresentationStyle.OverCurrentContext;
}
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
SetElementSize (new Size (View.Bounds.Width, View.Bounds.Height));
}
}
2. HostPage
public class ModalHostPage : ContentPage, IModalHost
{
#region IModalHost implementation
public Task DisplayPageModal(Page page)
{
var displayEvent = DisplayPageModalRequested;
Task completion = null;
if (displayEvent != null)
{
var eventArgs = new DisplayPageModalRequestedEventArgs(page);
displayEvent(this, eventArgs);
completion = eventArgs.DisplayingPageTask;
}
// If there is no task, just create a new completed one
return completion ?? Task.FromResult<object>(null);
}
#endregion
public event EventHandler<DisplayPageModalRequestedEventArgs> DisplayPageModalRequested;
public sealed class DisplayPageModalRequestedEventArgs : EventArgs
{
public Task DisplayingPageTask { get; set;}
public Page PageToDisplay { get; }
public DisplayPageModalRequestedEventArgs(Page modalPage)
{
PageToDisplay = modalPage;
}
}
}
3. HostPage renderer
public class ModalHostPageRenderer: PageRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if(e.OldElement as ModalHostPage != null)
{
var hostPage = (ModalHostPage)e.OldElement;
hostPage.DisplayPageModalRequested -= OnDisplayPageModalRequested;
}
if (e.NewElement as ModalHostPage != null)
{
var hostPage = (ModalHostPage)e.NewElement;
hostPage.DisplayPageModalRequested += OnDisplayPageModalRequested;
}
}
void OnDisplayPageModalRequested(object sender, ModalHostPage.DisplayPageModalRequestedEventArgs e)
{
e.PageToDisplay.Parent = this.Element;
var renderer = RendererFactory.GetRenderer (e.PageToDisplay);
e.DisplayingPageTask = this.PresentViewControllerAsync(renderer.ViewController, true);
}
}
Then it is as simple as calling
await ModalHost.DisplayPageModal(new PopUpPage());
from your host page or in this particular case from the ViewModel behind.
What Pete said about PushModalAsync / PopModalAsync still remains valid for this solution too (which in my opinion is not a disadvantage), but your popup would appear with transparent background.
The main advantage of this approach, in my opinion, is that you can have your popup XAML/code definition separate from the host page and reuse it on any other page where you wish to show that popup.
The general purpose of what you are trying to achieve can be accomplished by using the PushModalAsync and PopModalAsync methods of Xamarin.Forms Navigation object.
The chances are that this is good enough for what you are needing - However - this isn't truely modal. I will explain after a small code snippet:-
StackLayout objStackLayout = new StackLayout()
{
};
//
Button cmdButton_LaunchModalPage = new Button();
cmdButton_LaunchModalPage.Text = "Launch Modal Window";
objStackLayout.Children.Add(cmdButton_LaunchModalPage);
//
cmdButton_LaunchModalPage.Clicked += (async (o2, e2) =>
{
ContentPage objModalPage = new ContentPage();
objModalPage.Content = await CreatePageContent_Page2();
//
await Navigation.PushModalAsync(objModalPage);
//
// Code will get executed immediately here before the page is dismissed above.
});
//
return objStackLayout;
private async Task<StackLayout> CreatePageContent_Page2()
{
StackLayout objStackLayout = new StackLayout()
{
};
//
Button cmdButton_CloseModalPage = new Button();
cmdButton_CloseModalPage.Text = "Close";
objStackLayout.Children.Add(cmdButton_CloseModalPage);
//
cmdButton_CloseModalPage.Clicked += ((o2, e2) =>
{
this.Navigation.PopModalAsync();
});
//
return objStackLayout;
}
The problem with the above is that the
await Navigation.PushModalAsync(objModalPage);
will immediately return after the animation.
Although you can't interact with the previous page, as we are displaying a new NavigationPage with a Close button shown - the parent Navigation Page is still executing behind the scenes in parallel.
So if you had any timers or anything executing these still would get called unless you stopped those.
You could also use the TaskCompletionSource approach as outlined in the following post also How can I await modal form dismissal using Xamarin.Forms?.
Note - that although you can now await the 2nd page displaying and then when that page is dismissed allowing code execution to continue on the next line - this is still not truely a modal form. Again timers or anything executing still will get called on the parent page.
Update 1:-
To have the content appear over the top of existing content then simply include it on the current page, however make this section of content invisible until you need it.
If you use an outer container such like a Grid that supports multiple child controls in the same cell, then you will be able to achieve what you want.
You will also want to use something like a filled Box with transparency that will cover the entire page also, to control the visible, see through section, that surrounds your inner content section.
I followed above approach and found it impossible to run on iOS 7.
I found this library BTProgressHUD which you can modify and use.
I Use its methods by Dependency service.
Actual library for popups.
https://github.com/nicwise/BTProgressHUD
Following example uses BTProgressHUD library internally.
https://github.com/xximjasonxx/ScorePredictForms

Approach for designing View in vaadin 7

I am newbie to vaadin. I have to develop PoC on vaadin. Service layer is already written using spring. As a part of Poc I have to develop a screen below.
When request comes to my UI class, it will call my View using navigator. This view consists of one tabsheet and each tab have its own functionality and depends on other tab values too. First tab is search tab. It displays all the records came from db in the tab content area(Table/Grid addon. I dont know what to use). Each record have access to other two tabs. The other two tabs has fields to map each record's property. As of now, i have taken dummy data to display.
I wrote the view like this . But I am confused weather this approach is correct or not.
#VaadinView(UserView.NAME)
public class UserView extends VerticalLayout implements View {
public static final String NAME = "user";
public UserView(){
// For Tabs
TabSheet tabs = new TabSheet();
// first tab component
VerticalLayout layout = new VerticalLayout();
// for search fields
HorizontalLayout searchArea = new HorizontalLayout();
FormLayout searchAreaName = new FormLayout();
TextField name = new TextField("name");
FormLayout searchAreaEmail = new FormLayout();
TextField email = new TextField("email");
searchAreaName.addComponent(name);
searchAreaEmail.addComponent(email);
searchArea.addComponent(searchAreaName);
searchArea.addComponent(searchAreaEmail);
// for search table
BeanContainer<String, test.User> users = new BeanContainer<String, User>(
User.class);
users.setBeanIdProperty("userId");
users.addBean(new User("sudheer", "sudheer#kewil.com", "1"));
users.addBean(new User("sridhar", "sridhar#kewil.com", "2"));
users.addBean(new User("ranga", "ranga#kewil.com", "3"));
Table table = new Table("", users);
table.setSizeFull();
table.setPageLength(6);
layout.addComponent(searchArea);
layout.addComponent(table);
Tab tabOne = tabs.addTab(layout, "User Search", null);
// second tab component
VerticalLayout userLayout = new VerticalLayout();
userLayout.addComponent(new TextField("user name"));
userLayout.addComponent(new TextField("email"));
tabs.addTab(userLayout, "main details", null);
// tab change event
addComponent(tabs);
tabs.setHeight("50%");
tabs.setWidth("50%");
setComponentAlignment(tabs, Alignment.MIDDLE_CENTER);
}
#Override
public void enter(ViewChangeEvent event) {
}
}
I haven't implemented pagination also. Before going forward, I would like to know any other best approaches to go ahead.
Any suggestions would help me very much. Thanks in advance.
Anybody.. please help me out. I am going blindly with my appproach
Here is what I do in such cases:
Use the Blackboard event bus to fire events. These events carry a payload that essentially is the id of the record clicked/selected.
The other tabs or views are registered as a listener of this event. When the event is fired, the listeners extract the record/entity id from the payload, fetch the entity object from the back-end, and display it accordingly.
This approach ensures loosely-coupled components.
I hope it helps.

Resources