How to Remove Element / Component from BlazorWebassambly - razor

I am trying to make a Razor component that removes itself from the page after 3 seconds, after its added to the page.
I will click on a button
Than the Component will be added to the current page
After 3 seconds the Component removes itselfrom the page
<**div #ref="messageRef" style="position: absolute; margin: 0 auto; background-color: red; width: 200px; height: 80px;">**
<p>Message.....</p>
</div>
#code {
ElementReference messageRef;
private MessageComponent thisMsg;
protected override async Task OnInitializedAsync()
{
await JSRuntime.InvokeVoidAsync("MessageComponent.Remove()", messageRef);
StateHasChanged();
}
}

As JeremyW mentions in his comment, this can be done with an #if statement in the body of the page that holds the content. Using the Blazor template app as an example, it might look something like this:
#page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
#if (displayPrompt)
{
<SurveyPrompt Title="How is Blazor working for you?" />
}
#code {
private bool displayPrompt;
protected override void OnInitialized()
{
displayPrompt = true;
HideMessageAfterDelay();
base.OnInitialized();
}
private async void HideMessageAfterDelay()
{
await Task.Delay(3000);
displayPrompt = false;
StateHasChanged();
}
}
When displayPrompt evaluates to true, the prompt is added to the DOM. When is evaluates to false, it's removed from the DOM.
If you need the message component to handle this itself, then just put the equivalent code inside the component itself instead of the page.

Related

Routing to named element in Blazor (use anchor to navigate to specific element)

I cannot use an HTML anchor to navigate to a specific HTML element of a page in the Blazor Server. For example:
#page "/test"
<nav>
<!-- One version I've tried -->
Section2
<!-- Another version I've tried -->
<NavLink href="#section2">Section2</NavLink>
</nav>
#* ... *#
<h2 id="section2">It's Section2.</h2>
#* ... *#
When I click the link to Section2, I get redirected to the route http://localhost:5000/test#section2, however, will be at the top of the page. In my opinion, the browser should scroll down to the proper element, as specified by the Element Selector, but it can't.
Does it have to be done in a special way in Blazor?
I use Blazor 6 in .Net6 with Visual Studio 2022 (ver:17.0.2).
After loading a page, a browser automatically scrolls to the element identified by its id in the fragment part of the URL. It does the same when you click on an anchor with an href of the kind #element-id.
The page load behavior doesn't work for a Blazor Server because the element doesn't exist yet on page load.
The solution is to manually create a scroller using javascript and a razor component:
First of all, create a razor component like this
#inject IJSRuntime JSRuntime
#inject NavigationManager NavigationManager
#implements IDisposable
#code {
protected override void OnInitialized()
{
NavigationManager.LocationChanged += OnLocationChanged;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await ScrollToFragment();
}
public void Dispose()
{
NavigationManager.LocationChanged -= OnLocationChanged;
}
private async void OnLocationChanged(object sender, LocationChangedEventArgs e)
{
await ScrollToFragment();
}
private async Task ScrollToFragment()
{
var uri = new Uri(NavigationManager.Uri, UriKind.Absolute);
var fragment = uri.Fragment;
if (fragment.StartsWith('#'))
{
// Handle text fragment (https://example.org/#test:~:text=foo)
// https://github.com/WICG/scroll-to-text-fragment/
var elementId = fragment.Substring(1);
var index = elementId.IndexOf(":~:", StringComparison.Ordinal);
if (index > 0)
{
elementId = elementId.Substring(0, index);
}
if (!string.IsNullOrEmpty(elementId))
{
await JSRuntime.InvokeVoidAsync("BlazorScrollToId", elementId);
}
}
}
}
Then add this javascript code somewhere before the Blazor script renders. You can wrap it with script tags and place it in the head.
function BlazorScrollToId(id) {
const element = document.getElementById(id);
if (element instanceof HTMLElement) {
element.scrollIntoView({
behavior: "smooth",
block: "start",
inline: "nearest"
});
}
}
Finally implement it in your pages if needed. You can also place it inside your layouts, so it will work for every page you create.
#page "/"
<PageTitle>Index</PageTitle>
<a href="#my-id">
<h1>Hello, world!</h1>
</a>
<SurveyPrompt Title="How is Blazor working for you?" />
<div style="height: 2000px">
</div>
<div id="my-id">
Hello!
</div>
<AnchorNavigation />
Source: link
You can also use an ElementReference and FocusAsync which uses the built in Blazor JS. To use it you need to use a small hack to make the component "Focusable" which is to set a tabindex. I've used a span but you can use what you like. I've used #alessandromanzini's code to get the element from the NavigationManager.
Here's a component:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.Routing;
using System.Diagnostics.CodeAnalysis;
namespace SO75358165;
public class Bookmark : ComponentBase, IDisposable
{
private bool _setFocus;
[Inject] private NavigationManager NavManager { get; set; } = default!;
[Parameter] public RenderFragment? ChildContent { get; set; }
[Parameter] public string? BookmarkName { get; set; }
[DisallowNull] public ElementReference? Element { get; private set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "span");
builder.AddAttribute(2, "tabindex", "-1");
builder.AddContent(3, this.ChildContent);
builder.AddElementReferenceCapture(4, this.SetReference);
builder.CloseElement();
}
protected override void OnInitialized()
=> NavManager.LocationChanged += this.OnLocationChanged;
protected override void OnParametersSet()
=> _setFocus = this.IsMe();
private void SetReference(ElementReference reference)
=> this.Element = reference;
private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
{
if (this.IsMe())
{
_setFocus = true;
this.StateHasChanged();
}
}
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (_setFocus)
await this.Element!.Value.FocusAsync(false);
_setFocus = false;
}
private bool IsMe()
{
string? elementId = null;
var uri = new Uri(this.NavManager.Uri, UriKind.Absolute);
if (uri.Fragment.StartsWith('#'))
{
elementId = uri.Fragment.Substring(1);
return elementId == BookmarkName;
}
return false;
}
public void Dispose()
=> NavManager.LocationChanged -= this.OnLocationChanged;
}
Here's my test page:
#page "/"
<PageTitle>Index</PageTitle>
<NavLink href="#me">To me</NavLink>
<h1>Hello, world!</h1>
<h1>Hello, world!</h1>
<h1>Hello, world!</h1>
//.....
<h1>Hello, world!</h1>
<Bookmark BookmarkName="me" >
<h1 id="me">Focus on Me</h1>
</Bookmark>

HTML Portal - Failed to Execute 'activate' on 'HTMLPortalElement' Portal Element is not associated with a portal context

I'm trying to create a HTMLPortalElement with Lit-Element, but when i'm ready to activate() the Portal i get this Error in Web Dev Console :
DOMException: Failed to execute 'activate' on 'HTMLPortalElement': The HTMLPortalElement is not associated with a portal context.
class WikiPortal extends LitElement {
static get properties() { return {
_portalSrc: String
}};
constructor() {
super();
this._portalSrc = 'https://wicg.github.io/portals/';
}
render() { return html`
<portal src="${this._portalSrc}" #click="${this._portalClickHandler}">
</portal>`;
}
_portalClickHandler() {
this.shadowRoot.querySelector('portal').activate();
}
Before answering I just want to mention that portal is an experimental element and is only available in Chrome Canary after activating it by visiting chrome://flags/#enable-portals.
There seems to be an issue with the creation of the portal element within a template/shadowDOM. The following is a workaround that worked for me. It is basically creating the portal element programmatically in the main DOM and appending it as a child to the custom element. Note I added a test button within the component so I could see something to click.
import { LitElement, html, css } from 'lit-element';
class TestElement extends LitElement {
constructor() {
super();
this._portalSrc = 'https://wicg.github.io/portals/';
}
render() {
const template = html`<button type="button" #click="${this._portalClickHandler}">TEST</button>`;
return template;
}
_portalClickHandler() {
var node = document.createElement("portal");
node.src = this._portalSrc;
document.querySelector('test-element').appendChild(node);
console.log(document.querySelector('portal').src);
document.querySelector('portal').activate();
}
}
customElements.define('test-element', TestElement);

PlayN application in IFrame doesn't have keyboard input

I have made my first PlayN app and it works just great until it is embedded somewhere with iframe.
In my init() function I have this code for keyboard:
Keyboard k = PlayN.keyboard();
k.setListener(new Listener() {
#Override
public void onKeyUp(Event event) {
}
#Override
public void onKeyDown(Event event) {
/* long long code was here */
}
#Override
public void onKeyTyped(TypedEvent event) {
}
});
This works well, but not with iframe. It simply doesn't have the focus or something.
I found a workaround for this: quickly press F5 and quickly ckick the iframe few times before it is loaded. But I want something to do it automatically.
Examples: with iframe (this have input problems), no iframe (this works well).
I've had this issue as well. I believe the fix that worked for me was adding the following to the main .html file:
<script src="mygamesource.nocache.js"></script>
<!-- right below your nocache script -->
<script>
function handleMouseDown(evt) {
window.focus();
evt.preventDefault();
evt.stopPropagation();
evt.target.style.cursor = 'default';
}
document.getElementById('body').addEventListener('mousedown', handleMouseDown, false);
</script>
Credit to this post if it works for you.

GWT Responsive Design solution

i am working on GWT to make web application but now i need to make my web applications in Responsive design but GWT not support Responsive Design please help me.
GWT supports responsive design just like any other widget toolkit (OK, that's not exactly true, there are probably widget toolkits that do a better job): do your layout with HTMLPanel, FlowPanel, SimplePanel and responsive CSS, or go the active layout route with layout panels and doing calculations in your code (or in a custom layout panel).
You can make your layout responsive using CSS media queries.
For example, to make dialog boxes occupy 90% of the available horizontal space on devices that have screen size up to 640px, one can wrap the style inside #media block like this:
#media all and (max-width: 640px) {
.gwt-DialogBox {
width: 90%;
}
}
Unfortunately (as of today) GWT compiler does not support media CSS, so the code above will fail if you use it in conjunction with CssResource.
One of the approaches to this problem is to split your CSS resources into two files.
All default (desktop) CSS styles would go to the first file (e.g. main.css), and all your mobile overrides would go to the second file (e.g. mobile.css).
Note that style names that you want to override for mobile need to be tagged as #external in the main.css to avoid name obfuscation by gwt compiler.
main.css:
#external .mainNorthPanel;
.mainNorthPanel {
position: fixed;
top: 0px;
}
mobile.css:
#media all and (max-width: 640px) {
.mainNorthPanel {
position: absolute;
top: -3em;
}
}
In your application ClientBundle, use main.css in conjunction with your CssResource interface, and define the mobile file as an external resource:
public interface MyBundle extends ClientBundle {
public interface MainStyles extends CssResource {
String mainNorthPanel();
}
#Source("css/main.css")
MainStyles css();
#Source("css/mobile.css")
TextResource mobile();
}
And finally inject your external CSS resource somewhere in the module initialization:
String mobileCss = myBundle.mobile().getText();
StyleInjector.injectAtEnd(mobileCss)
For the full working example, take a look at this JavaWorld post that just came out recently:
http://www.javaworld.com/article/2842875/java-web-development/responsive-web-design-with-google-web-toolkit.html
It covers some basic concepts such css-based responsive views, dialogs, and menus.
And there is a little proof of concept on github:
https://github.com/cuppafame/gwt-responsive
In addition to what Thomas said you can look into gwt-bootstrap. They have a custom DataGrid widget that can be shown or hidden based on breakpoints (tablets, phones, etc).
If you actually want to hide and show columns based on available size you extend the DataGrid and do something along this lines:
ResponsiveDataGrid extends DataGrid<myDTO> {
private final Column<myDTO,String> column1;
private final Column<myDTO,String> column2;
private Boolean isCompact;
public ResponsiveDataGrid(int pageSize, Resources resources,ActionCell.Delegate<myDTO> actionDelegate) {
super(pageSize, resources,new EntityProxyKeyProvider<myDTO>());
initColumns();
}
private void initColumns() {
// init your columns
}
private void updateColumns() {
int columnCount = getColumnCount();
for (int i =columnCount-1;i>=0;i--) {
removeColumn(i);
}
removeUnusedColGroups();
if (isCompact) {
// show columns for compact size
}
else {
// show all columns
}
}
#Override
protected int getRealColumnCount() {
return getColumnCount();
}
// WORKAROUND for some sizing issues in DataGrid
private void removeUnusedColGroups() {
int columnCount = getColumnCount();
NodeList<Element> colGroups = getElement().getElementsByTagName("colgroup");
for (int i = 0; i < colGroups.getLength(); i++) {
Element colGroupEle = colGroups.getItem(i);
NodeList<Element> colList = colGroupEle.getElementsByTagName("col");
for (int j = colList.getLength()-1;j>=0; j--) {
colGroupEle.removeChild(colList.getItem(j));
}
}
}
#Override
public void onResize() {
super.onResize();
if (!isAttached()) {
return;
}
// or whatever breakpoint you want to support
boolean isNewCompact = (getOffsetWidth() < 800);
if (isCompact == null || isNewCompact != isCompact) {
isCompact = isNewCompact;
updateColumns();
}
}
}
/*The best way to do responsive web with is to use Timer class and Window class in GWT as Gwt does not responsive web at the moment. I have been searching on the web for about a week now and it was a waist of my time, even Google does not know how to do that. I came out with a very straight forward solution by using Window class and Timer class and it works like a magic.*/
public class View extends VerticalPanel{
private FlexTable flexTable=new FlexTable();
private Button[]buyAndSellButtons = new Button[2];
private TextBox[] textField=new TextBox[2];
private Label alert=new Label();
private LinkedList <String> stock=new LinkedList<>();
public View(){
createComponents();
}
public VerticalPanel createComponents() {
// Assume that the host HTML has elements defined whose
// IDs are "slot1", "slot2". In a real app, you probably would not want
// to hard-code IDs. Instead, you could, for example, search for all
// elements with a particular CSS class and replace them with widgets.
//
HorizontalPanel[] horizontalPanel = new HorizontalPanel[4];
for (int x = 0; x < horizontalPanel.length; x++) {
horizontalPanel[x] = new HorizontalPanel();
}
alert.setStyleName("alert");
add(alert);
flexTable.setText(0, 0, "BUY Orders");
flexTable.getCellFormatter().setStyleName(0, 0, "orderMatcherListHeader");
flexTable.setText(0, 1, "SELL Orders");
flexTable.getCellFormatter().setStyleName(0, 1, "orderMatcherListHeader");
flexTable.setStyleName("flexTable");
flexTable.setWidth("33em");
flexTable.setCellSpacing(5);
flexTable.setCellPadding(3);
add(flexTable);
Label[] labels = new Label[2];
labels[0] = new Label("Volume");
labels[1] = new Label("Price");
for (Label label : labels) {
label.setStyleName("label");
horizontalPanel[1].add(label);
horizontalPanel[1].setStyleName("labelPosition");
}
textField[0] = new TextBox();
textField[0].setTitle("Volume");
textField[1] = new TextBox();
textField[1].setTitle("Price");
for (TextBox textBox : textField) {
textBox.setStyleName("textField");
textBox.setFocus(true);
horizontalPanel[2].add(textBox);
}
buyAndSellButtons[0] = new Button("BUY");
buyAndSellButtons[1] = new Button("SELL");
for (Button button : buyAndSellButtons) {
horizontalPanel[3].add(button);
button.setStyleName("buttons");
horizontalPanel[3].setStyleName("buttonPosition");
}
VerticalPanel[] mainPanel = new VerticalPanel[1];
mainPanel[0] = new VerticalPanel();
for (HorizontalPanel aHorizontalPanel : horizontalPanel) {
mainPanel[0].add(aHorizontalPanel);
mainPanel[0].setStyleName("mainPanel_1");
setStyleName("mainPanel");
add(mainPanel[0]);
Window.addResizeHandler(new ResizeHandler() {
#Override
public void onResize(ResizeEvent event) {
alert.setText("" + Window.getClientWidth());
}
});
}
Timer timer=new Timer() {
int x;
#Override
public void run() {
alert.setText(Window.getClientWidth()+"Attach" + x++);
String[] gadget=new String[10];
gadget[0]=("354"); //android portrait
gadget[1]=("625");
gadget[2]=("314");
gadget[3]=("474");
gadget[4]=("369");
gadget[5]=("562");
gadget[6]=("617");// android landscape
gadget[7]=("48");
gadget[8]=("730");
alert.setText("" + Window.getClientWidth()+x++);
if(Window.getClientWidth()<=425) {
flexTable.getCellFormatter().setStyleName(0, 0, "orderMatcherListHeader_1");
flexTable.getCellFormatter().setStyleName(0, 1, "orderMatcherListHeader_1");
mainPanel[0].setStyleName("phonePortrait_1");
setStyleName("phonePortrait");
flexTable.setStyleName("flexTable_1");
if(Window.getClientWidth()==414){
flexTable.setWidth("26.2em");
}{
flexTable.setWidth("24.2em");
}
flexTable.setCellSpacing(5);
flexTable.setCellPadding(3);
}
else if((Window.getClientWidth()>425)&&(Window.getClientWidth()<=812)) {
alert.setText("" + Window.getClientWidth());
flexTable.getCellFormatter().setStyleName(0, 0, "orderMatcherListHeader_1");
flexTable.getCellFormatter().setStyleName(0, 1, "orderMatcherListHeader_1");
mainPanel[0].setStyleName("phoneLandScape_1");
setStyleName("phoneLandScape");
flexTable.setStyleName("flexTable_1");
flexTable.setWidth("24.2em");
flexTable.setCellSpacing(5);
flexTable.setCellPadding(3);
}else {
return;
}
}
};
timer.scheduleRepeating(500);
return this;
}

Flex: Does anybody know a TabNavigator component that allows for html labels in the tabs or how to do this?

I'm trying to put html-formatted labels in the tabs of a TabNavigator.
I saw SuperTabNavigator in the FlexLib but it doesn't seem to do the trick for me.
I found this html button code and was able to inject my own TabBar and have it change the class instantiated by the ClassFactory when a navItem is created.
HtmlTabNavigator:
public class HtmlTabNavigator extends TabNavigator
{
public function HtmlTabNavigator()
{
super();
}
override protected function createChildren():void
{
if (!tabBar)
{
tabBar = new HtmlTabBar(); // inject my class
tabBar.name = "tabBar";
tabBar.focusEnabled = false;
tabBar.styleName = new StyleProxy(this, tabBarStyleFilters);
rawChildren.addChild(tabBar);
if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_3_0)
{
tabBar.setStyle("paddingTop", 0);
tabBar.setStyle("paddingBottom", 0);
tabBar.setStyle("borderStyle", "none");
}
}
super.createChildren(); // ommits original TabBar creation but continues in inheritance chain
}
public function setHtmlLabels( htmlLabels:Array ):void
{
for (var i:uint = 0; i < tabBar.numChildren; i++)
{
var button:Button = tabBar.getChildAt( i ) as Button;
button.label = htmlLabels[ i ];
}
}
}
HtmlTabBar:
public class HtmlTabBar extends TabBar
{
public function HtmlTabBar()
{
super();
navItemFactory = new ClassFactory(HtmlButton);
}
}
Now I'm having problems with the style of the button as it looks like a regular button and not like a tab anymore. It is not apparent to me why this works when a ButtonBarButton is used.
Any ideas are welcome.
Thanks
Stefan
Like you mentioned, I think its best to built your own component for this scenario. You could consider using a textArea with editable=false for the tab portion since I believe htmlText can be displayed in that component.
Edit: Can you maybe modify the SuperTabNavigator and add in a textArea... so the original label for the tab could be blank (if you cant remove it) then have the textArea on top of it.
With best regards,
Following your example, I tried to specialize the TabBar to use a Custom Button with a Mnemonic Label functionality, but I get a compile time error:
1178: Attempted access of inaccessible
property navItemFactory through a
reference with static type
AspMnemonicTabBar.
Reading the Flex Source Code I found it belongs to a private namespace mx_internal. So how could I access it to set a new ClassFactory ??
package
{
import mx.controls.TabBar;
import mx.core.ClassFactory;
public class AspMnemonicTabBar extends TabBar
{
public function AspMnemonicTabBar()
{
super();
navItemFactory = new ClassFactory(AspMnemonicButton);
}
}
}
Alessandro, navItemFactory is in namespace mx_internal. Access it through mx_internal::navItemFactory, or put the following line below your import statements:
use namespace mx_internal;