MatFormField outline issue - angular-material

When I change the font type in my form, label overlays the outline.
How can i fix this?
in the following image it is shown how it should be:

Seems like after 3 years there's still no fix to this issue in Angular Material.
However, there are two workarounds:
First workaround:
create a directive that links to all mat-form-field components (directives) with appearance attribute set to outline
listen to document.fonts.ready event and run updateOutlineGap on the MatFormField
export the directive in AppModule or SharedModule to make it accessible everywhere
this way Angular will update the outline size as soon as the custom font is loaded
import { AfterViewInit, Directive } from '#angular/core';
import { MatFormField } from '#angular/material/form-field';
#Directive({
selector: 'mat-form-field[appearance=outline]',
})
export class UpdateOutlineGapDirective implements AfterViewInit {
constructor(private formField: MatFormField) {
}
ngAfterViewInit() {
document.fonts.ready.then(() => {
this.formField.updateOutlineGap();
});
}
}
Second workaround:
add #userNameField to your mat-form-field element
add (focus)="userNameField.updateOutlineGap()" to the input element
this way every time the input is focused, Angular will update the outline size

Related

Vaadin Designer Tabs not correctly adding children "tab" to tabs object

I'm using Vaadin Deisgner 14.6.1 to create some super simple tabs. However, when I try to do some simple operations in the java class (eg selecting a tab), it throws an error which indicates that the "Tabs" object does not have the proper children "tab" components. Here's a simple test case below. (I discovered the issue when I was trying to add a addSelectedChangeListener() to the tabs class and discovered that it would never fire, presumably since the "tabs" class never properly had any children.) I tried a bunch of hacks, but nothing worked. (I have in the past gotten tabs to work if I stuck purely to a programmatic approach, but I really really really like using Designer, since it saves me tonnes of times and keeps the code quite modular and clean....when it works....)
import {html, PolymerElement} from '#polymer/polymer/polymer-element.js';
import '#vaadin/vaadin-ordered-layout/src/vaadin-vertical-layout.js';
import '#vaadin/vaadin-tabs/src/vaadin-tabs.js';
import '#vaadin/vaadin-tabs/src/vaadin-tab.js';
class MyTabtest extends PolymerElement {
static get template() {
return html`
<style include="shared-styles">
:host {
display: block;
height: 100%;
}
</style>
<vaadin-vertical-layout theme="spacing" style="width: 100%; height: 100%;">
<vaadin-tabs theme="equal-width-tabs" id="tabs" orientation="horizontal" selected="0">
<vaadin-tab id="tab1" selected>
Tab one
</vaadin-tab>
<vaadin-tab id="tab2">
Tab two with a longer title
</vaadin-tab>
<vaadin-tab id="tab3">
Tab three
</vaadin-tab>
</vaadin-tabs>
<label id="lbl1">page1</label>
<label id="lbl2">page2</label>
<label id="lbl3">page3</label>
</vaadin-vertical-layout>
`;
}
static get is() {
return 'my-tabtest';
}
static get properties() {
return {
// Declare your properties here.
};
}
}
customElements.define(MyTabtest.is, MyTabtest);
and
package com.deepsearch.fe.tab2vizdb.fpsgraphicaldetails.spectratab.hslspectrachartandalts;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.polymertemplate.Id;
import com.vaadin.flow.component.tabs.Tab;
import com.vaadin.flow.component.tabs.Tabs;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.templatemodel.TemplateModel;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.polymertemplate.PolymerTemplate;
/**
* A Designer generated component for the my-tabtest template.
*
* Designer will add and remove fields with #Id mappings but
* does not overwrite or otherwise change this file.
*/
#Route("tabtest")
#Tag("my-tabtest")
#JsModule("./src/my-tabtest.js")
public class MyTabtest extends PolymerTemplate<MyTabtest.MyTabtestModel> {
#Id("tabs")
private Tabs tabs;
#Id("tab1")
private Tab tab1;
#Id("tab2")
private Tab tab2;
#Id("tab3")
private Tab tab3;
#Id("lbl1")
private Label lbl1;
#Id("lbl2")
private Label lbl2;
#Id("lbl3")
private Label lbl3;
/**
* Creates a new MyTabtest.
*/
public MyTabtest() {
// tabs.setSelectedTab(tab2); //throws error!
tabs.addSelectedChangeListener(e -> {
System.out.println("A tab got selected!"); //this never fires!!!!
});
}
/**
* This model binds properties between MyTabtest and my-tabtest
*/
public interface MyTabtestModel extends TemplateModel {
// Add setters and getters for template properties here.
}
}
Ultimately, I'm trying to capture a tab select event -- but it doens't seem to work when the tabs are created in Designer....is this true on Vaadin's side too? (ie is this reproducible?)
This is an unfortunate limitation of the component mapping to elements defined in a template. When mapping to Java, the parent-child relationships are not preserved and thus the tabs component does not realize that the tab is one of its child components.
See https://github.com/vaadin/flow/issues/7622
The way to make it work would be to create the Tabs and Tab instances in Java and the rest in Designer.

Adding CSS class to Global Overlay in Angular 9 at runtime

I am creating an Angular 9 web-app that changes theme as per the choice of the user, akin to material.angular.io/. To handle the resulting transparent background of mat-select, I then referred to the
Angular material theming guide , to add my custom theme classes in the global overlay container as follows.
export class AppModule {
constructor(overlayContainer: OverlayContainer) {
overlayContainer.getContainerElement().classList.add('purple-amber');
overlayContainer.getContainerElement().classList.add('indigo-pink');
overlayContainer.getContainerElement().classList.add('pink-bluegrey');
overlayContainer.getContainerElement().classList.add('purple-green');
}
}
However, the mat-select element always has the last added class (in this case, purple-green) as it's overlay, regardless of what theme I select. How could I solve this? Is there a way through which I could add the theme to the classList through a component's TypeScript file?
Lets say you have 2 themes: "purple-amber" and "indigo-pink", those themes change by clicking a button.
You need call a function when those button are clicked.
some-component.html:
...
<button mat-raised-button (click)="applyPurpleTheme()">Purple Amber</button>
<button mat-raised-button (click)="applyIndigoTheme()">Indigo Pink</button>
...
some-component.ts
...
constructor(private OverlayContainer: OverlayContainer) { }
applyPurpleTheme() {
const overlayContainerClasses = this.OverlayContainer.getContainerElement().classList;
// I always remove other theme classes
if (overlayContainerClasses.contains('indigo-pink')) {
overlayContainerClasses.remove('indigo-pink');
}
overlayContainerClasses.add('purple-amber');
}
applyIndigoTheme() {
const overlayContainerClasses = this.OverlayContainer.getContainerElement().classList;
// I always remove other theme classes
if (overlayContainerClasses.contains('purple-amber')) {
overlayContainerClasses.remove('purple-amber');
}
overlayContainerClasses.add('indigo-pink');
}
...

Vaadin Tabs based component created using Designer doesn't show data when bound using its Java companion file

Below is the Vaadin Designer code for simple tab functionality
import {html, PolymerElement} from '#polymer/polymer/polymer-element.js';
import '#vaadin/vaadin-tabs/src/vaadin-tabs.js';
import '#vaadin/vaadin-tabs/src/vaadin-tab.js';
class TestUi extends PolymerElement {
static get template() {
return html`
<style include="shared-styles">
:host {
display: block;
height: 100%;
}
</style>
<vaadin-tabs theme="equal-width-tabs" id="vaadinTabs">
<vaadin-tab id="vaadinTab">
Product Overview
</vaadin-tab>
<vaadin-tab id="vaadinTab1">
Product DetailView
</vaadin-tab>
<vaadin-tab id="vaadinTab2">
Reports
</vaadin-tab>
</vaadin-tabs>
`;
}
static get is() {
return 'test-ui';
}
static get properties() {
return {
// Declare your properties here.
};
}
}
customElements.define(TestUi.is, TestUi);
It's corresponding Java companion file looks as below
import com.vaadin.flow.component.polymertemplate.Id;
import com.vaadin.flow.component.tabs.Tab;
import com.vaadin.flow.component.tabs.Tabs;
import com.vaadin.flow.templatemodel.TemplateModel;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.polymertemplate.PolymerTemplate;
/**
* A Designer generated component for the test-ui template.
*
* Designer will add and remove fields with #Id mappings but
* does not overwrite or otherwise change this file.
*/
#Tag("test-ui")
#JsModule("./src/productdetailview/test-ui.js")
public class TestUi extends PolymerTemplate<TestUi.TestUiModel> {
#Id("vaadinTabs")
private Tabs vaadinTabs;
#Id("vaadinTab")
private Tab vaadinTab;
#Id("vaadinTab1")
private Tab vaadinTab1;
#Id("vaadinTab2")
private Tab vaadinTab2;
/**
* Creates a new TestUi.
*/
public TestUi() {
// You can initialise any data required for the connected UI components here.
vaadinTabs.addSelectedChangeListener(selectedChangeEvent -> {
selectedChangeEvent.getSelectedTab().getElement().getStyle().set("background-color":"blue");
});
}
/**
* This model binds properties between TestUi and test-ui
*/
public interface TestUiModel extends TemplateModel {
// Add setters and getters for template properties here.
}
}
In the above code, My thinking was to start writing the selectedChangeListener Handler directly without doing much but instead this doesn't work and below initialization code needs to be added.
//I have added for one tab but it requires all the tabs to be added
vaadinTabs = new Tabs();
vaadinTab = new Tab();
vaadinTabs.add(vaadinTab);
My question here is why would I need to initialize when the Polymer js code generated using Vaadin Designer clearly defines the tab and it's group?
This is the same issue with Vaadin Grid. Even after defining the columns in the Polymer js, I have to redefine it from the Java component end instead of directly start providing the data via data provider
TLDR; Unfortunately, you have encountered this issue IllegalArgumentException when switching tabs
which is closed as won't fix.
My question here is why would I need to initialize when the Polymer js code generated using Vaadin Designer clearly defines the tab and it's group?
Generally, you don't need to. But Tabs doesn't work as intended in this case. Thus, for this particular component, it's suggested to not mix template/Java logic.
For example, you can verify it with a <vaadin-text-field>, where event is fired correctly.
Java counterpart
#Id("vaadinTextField")
private TextField vaadinTextField;
/**
* Creates a new TestUi.
*/
public TestUi() {
// You can initialise any data required for the connected UI components here.
vaadinTextField.addValueChangeListener(event->{
System.out.println("Event has happened");
});
vaadinTextField.setValueChangeMode(ValueChangeMode.EAGER);
and snippet for the template right after the tabs:
<vaadin-vertical-layout id="vaadinVerticalLayout" style="width: 100%; height: 100%;">
<vaadin-text-field id="vaadinTextField"></vaadin-text-field>
</vaadin-vertical-layout>
Taken from the issue:
So all Tab related API methods in Tabs are completely broken in regard to injected Tabs.
and
Unfortunately we've concluded that there is no sensible way we can support this for now, thus this issue will be a known limitation with Tabs. It will not work as #Id mapped component when the child vaadin-tabs are created in the template file, so you should not try to mix client & server logic and content for the Tabs component.
As a workaround, you could try to use your own component for #Id mapping tabs like:
#Tag("vaadin-tabs")
public IdMappedTabs extends Component {
public IdMappedTabs() {
}
public Registration addSelectionListener(PropertyChangeListener listener) {
return getElement().addPropertyChangeListener("selected", listener);
}
public void setSelectedTabIndex(int index) {
getElement().setProperty("selected", index);
}
}
Edit:
What is the issue with Grid you are having? (There is a good tutorial about Designer, where Grid is used. It might be useful : Vaadin Designer tutorial)

angular 7 how to access element by id defined in parent component from child component template

I am using primeng multiSelect control in the child component. i want this to be appended to the element defined in the parent component as shown above.
the above code failed when i run npm run build:prod. it can't access popupContent in the child. can anyone help how to access the parent element in the child component?
**Parent component:**
<tbody app-sg-add-edit-subline-agg (sublineSave)="onSublineSave($event)" #popupContent>
</tbody>
**sg-add-edit-subline-agg component:**
<p-multiSelect optionLabel="name"
[options]="sublines"
formControlName="name"
[appendTo]="popupContent">
Thanks
can anyone help how to access the parent element in the child component?
The Example shown below is from the Angular Documents
The parent will need to send the data using the #Input() functionality.
However, there are other ways to pass information from Child to Parent and Parent to Child like using a service. This is just a basic example.
Parent Component
import { Component } from '#angular/core';
import { HEROES } from './hero';
#Component({
selector: 'app-hero-parent',
template: `
<h2>{{master}} controls {{heroes.length}} heroes</h2>
<app-hero-child *ngFor="let hero of heroes"
[hero]="hero"
[master]="master">
</app-hero-child>
`
})
export class HeroParentComponent {
heroes = HEROES;
master = 'Master';
}
Child Component
import { Component, Input } from '#angular/core';
import { Hero } from './hero';
#Component({
selector: 'app-hero-child',
template: `
<h3>{{hero.name}} says:</h3>
<p>I, {{hero.name}}, am at your service, {{masterName}}.</p>
`
})
export class HeroChildComponent {
#Input() hero: Hero;
#Input('master') masterName: string;
}
Explanation:
We are using a reference template variable to obtain a reference of the parent using #popupComponent. And then we pass it to the child as an #Input() property.
Parent component:
<tbody app-sg-add-edit-subline-agg
(sublineSave)="onSublineSave($event)"
#popupContent [popupContent]="popupContent">
</tbody>
sg-add-edit-subline-agg.component.html:
<p-multiSelect optionLabel="name"
[options]="sublines"
formControlName="name"
[appendTo]="popupContent">
Make sure to add the #Input property in sg-add-edit-subline-agg.component.ts
#Input() popupContent;
StackBlitz with a simpler example of above concept: https://stackblitz.com/edit/primeng-parent-child?file=app%2Fapp.component.html

Use DartAngular with dart:html

Is it possible to use default dart library html with angular dart?
ie:
class Test1Component implements OnInit{
#override
void ngOnInit() {
ButtonElement button = querySelector('button');
//Broken code, avoid button to be null.
button.onClick.listen(onClick);
}
void onClick(Event e){
print('Button clicked');
}
}
How can I avoid to get a 'null' button without the using any timers?
Basically I'm using only angular just for the Routes and but I'd like to stick with dart:html to control the DOM and events.
Yes, you can do that, but it's usually not a good idea.
Use instead #ViewChild(...) or similar Angular methods to get references to elements in a components view.
<button #myButton>click me</button>
#ViewChildren('myButton')
set myButton(List<Element> value) {
if(value.isNotEmpty) {
print(value.first);
}
}
If you want to just add a click handler using
<button (click)="onClick">click me</button>
would be the better way but it sounds you are somehow adding the button dynamically and adding a click handler declaratively might not work in this case (would need more info)
EDIT:
If someone like me want to use dart:html instead angular ng code, it's possible to use it
import 'package:angular/angular.dart';
import 'dart:html';
// AngularDart info: https://webdev.dartlang.org/angular
// Components info: https://webdev.dartlang.org/components
#Component(
selector: 'container',
template: '<h1>Test 1</h1><button #test1>Bottone test 1</button>',
)
class Test1Component implements OnInit{
#ViewChild('test1')
ButtonElement button;
#override
void ngOnInit() {
//Verified that button is initialized
print(button);
//Initialize click
button.onClick.listen((e) => print("Clicked"));
}
}

Resources