I would like to connect an angular cdk overlay to a button in my toolbar.
That's what I got:
overlay-button.html:
<button mat-menu-item #overlayButton (click)="showOverlay()"> open </button>
overlay-button.component
export class OverlayButtonComponent {
#ViewChild('overlayButton') private _button: ElementRef;
constructor(private _overlay: OverlayService) {}
showOverlay() {
this._overlay.open(DisplayedComponent,this._button);
}
}
overlay.service
export class OverlayService {
constructor(private _overlay: Overlay) {}
open(comp: ComponentType<any>, connectedTo:ElementRef) {
const positionStrategy = this._overlay.position()
.flexibleConnectedTo(connectedTo)
.withPositions([{
originX: 'start',
originY: 'bottom',
overlayX: 'start',
overlayY: 'top',
}]);
const overlayRef = this._overlay.create({
hasBackdrop: true,
positionStrategy,
scrollStrategy: this._overlay.scrollStrategies.reposition()
});
// Create ComponentPortal that can be attached to a PortalHost
const filePreviewPortal = new ComponentPortal(comp);
// Attach ComponentPortal to PortalHost
overlayRef.attach(filePreviewPortal);
overlayRef.backdropClick().subscribe(() => overlayRef.detach());
}
}
The problem is that the overlay is always displayed in the top left corner of the Browser.
First i thought there is a problem with the viewChild, maybe the elementRef is undefined or something like that, but that is not the case. I tried every tutorial I found with similar code and nothing worked. Could the problem be anywhere else?
Here is an illustration of how it is:
The reference you get from your ViewChild is not actually ElementRef. You can wrap the button to a div and then get ViewChild reference to that.
Based on #joonas comment, modify your viewChild Code to be
#ViewChild('overlayButton', {read: ElementRef}) private _button: ElementRef;
forcing the viewChild to be read as an ElementRef, and thereby passed into the flexibleConnectedTo() call.
Related
Thanks for taking the time to review this matter.
So I've been going through this issue for a couple of days now. I'm trying to set up a navigation menu on my Microsoft Teams Side App. A set of personal tabs is exactly what it is. Following the documentation here: https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/design/personal-apps?view=msteams-client-js-1.12.1#use-a-personal-app-private-workspace
It explicitly shows how we can add a navigation bar at the top-right of our application:
Screenshot from MSTeams documentation
Simple, right?
Well, it doesn't seem to work like that. When I try and follow the SDK instructions to set the navigation bar using the function microsoftTeams.menus.setNavBarMenu() it doesn't display anything nor provide any sort of error message on the console
This is the code I have set up for my Angular component:
import {Component, OnInit} from "#angular/core"
import {app, menus, pages} from "#microsoft/teams-js"
import {Location} from '#angular/common'
#Component({
selector: 'my-app',
templateUrl: './my-app.html',
styleUrls: ['./my-app.scss'],
})
export class MyApp implements OnInit {
public initialized = false
public theme = 'default'
constructor(private location: Location) { }
ngOnInit(): void {
app.initialize().then((response) => {
this.initialized = true
app.getContext().then((context) => {
if (context.app.theme !== 'default') {
this.theme = 'dark'
}
})
app.registerOnThemeChangeHandler((theme: string) => {
if (theme !== 'default') {
this.theme = 'dark'
} else {
this.theme = 'default'
}
})
pages.backStack.registerBackButtonHandler(() => {
this.location.back()
return true
})
menus.initialize()
const item = new menus.MenuItem()
item.id = 'test'
item.icon = 'there is an <svg></svg> tag here but I shortened it for easier reading'
item.title = 'test'
item.displayMode = menus.DisplayMode.ifRoom
console.log(item)
menus.setNavBarMenu([item], (id) => {
console.log(id)
return true
})
})
}
}
Nothing fancy. Just an HTML template with some styling that you can ignore. This is just a mock testing app. The only difference is that I import the namespace directly.
So, everything else is working: Microsoft Teams is being initialized, the theme handler sets a default or dark theme, and the back button works as indicated. The only thing that's missing is the navBarMenu.
I tried with and without an Icon value inside the string, with and without the displayMode property, I tried it directly on the HTML parent using a script tag, tried the previous SDK version (1.16) and this one (2.5)
I'm pretty sure I'm missing something on this setup, but I can't seem to find any information regarding this SDK menus module and the documentaion isn't really helpful there (Or maybe I'm not smart enough to get it, yet)
Again, thank you so much for reading me. Any feedback on anything I'm doing wrong or could potentially improve is completly welcome
Cheers!
I have an issue with showing a Modal on Ionic 5 Angular 8 on iOS.
So I have a page with two buttons, one is declared in the same page component, the other one is from a child component.
When the button in the same page is clicked, the modal opens fine.
When the button from the child component is clicked, the event is emitted to the parent Page, which then creates the modal, but the modal slides up, outside the view port.
I have tried all possible trouble shooting I can think of:
Removing all custom CSS, removing all html, changing the route of the event, adding a timeout etc..
I don't have any autofocus or bootstrap: [] in my entire app.
Here is what the page looks like
export class MyPage implements OnInit, OnDestroy {
//...
private openModal (params) {
const obj = {
component: ModalComponent,
componentProps: params
};
this.modalProvider.create (obj).then (
modal => {
modal.onDidDismiss().then(
() => {}
);
modal.presente ();
}
)
}
public onOpenModal () {
const params = {
//custom properties
}
this.openModal(params);
}
public localAction () {
const params = {
// custom params
}
this.openModal (params)
}
}
Now on the template side :
<ion-content force-overscroll="false" [scrollY]="false">
<!-- Some html --->
<ion-grid (click)="localAction()">
<!-- Some html --->
</ion-grid>
<!-- Some html --->
<child-component (modalOpenEmitter)="onOpenModal($event)">
</ion-content>
Ok I have found the problem after several hours digging.
It turns out it was because of the child component which host a list of button scrolling horizontally.
When a button from the the child component was clicked, an event was emitted as per normal, but I was using a DOM function called scrollintoview like so:
Child component.ts :
public btnClicked (value) {
//...
this.clickEmitter.emit(value); // Normal stuff
const element = document.getElementById(value.id);
if (element) {
element.scrollIntoView(); // This caused the the modal to disappear
}
}
This scrollIntoView was there to make the clicked button slide further in the view port.
Unfortunately, this beaks the modal on iOS, but it works fine on android by the way.
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
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"));
}
}
Question - How to properly use React Navigation's TabNavigator container component inside another React component that acts just as wrapper component?
What I want to achieve - Basically I want the appbar and tabbar both to be displayed - appbar on the top, tabbar (TabBarTop) just beneath it, a very common design pattern.
I have tried a couple of ways.
Method #1 (Nesting inside StackNavigator)
Tab.js
export const Tab = TabNavigator({
Tab1: { screen: Tab1Container },
Tab2: { screen: Tab2Container }
}, {
tabBarComponent: TapBarTop,
tabBarPosition: 'top'
});
AppBar.js
class AppBarComponent extends Component {
static navigationOptions = { header: null }
render() {
return (
<View>
*some more views, buttons blah blah here
</View>
)
}
}
export default AppBarComponent;
and I use them inside StackNavigator, like
export default StackNavigator({
stack1: { screen: AppBarComponent },
stack2: { screen: Tab }
});
This results in only 1 stack to be displayed at a given time which is exactly how it works. And I don't have anything to do with initialRouteName.
Method #2 (wrapping inside another component)
<View style={{ flex: 1 }}>
<AppBarComponent />
<Tab />
</View>
This on the contrary displays both components but this.props.navigation.navigate('somepath') or push() or pop() or
replace() doesn't work from inside and . But this.props.navigation and its methods all are available inside those components.
PS - I'm using React Navigation v1 and running on iOS
Solution for Method 1
Have a StackNavigator with only one screen and show your TabNavigator in that screen. Then customize header with your custom AppBarComponent.
header
React Element or a function that given HeaderProps returns a React
Element, to display as a header. Setting to null hides header.
Sample
export default StackNavigator({
stack: {
screen: Tab,
navigationOptions: {
header: (HeaderProps) => (<AppBarComponent headerProps={HeaderProps} />)
}
}
});
Solution for Method 2
You can wrap your component which is not a part of the stack with withNavigation HOC.
withNavigation is a higher order component which passes the
navigation prop into a wrapped Component. It's useful when you cannot
pass the navigation prop into the component directly, or don't want to
pass it in case of a deeply nested child.
Sample
class AppBarComponent extends Component {
static navigationOptions = { header: null }
render() {
return (
<View>
*some more views, buttons blah blah here
</View>
)
}
}
export default withNavigation(AppBarComponent);
#bennygenel your answer is definitely useful in addressing the problem. I kind of fed wrong information when I talk about having components inside TabNavigator. Well, instead of dumb components, there were multiple redux containers inside the TabNavigator as screens and the entire TabNavigator wrapped in a Dumb component. I have edited my question sincerely.
Though this.props.navigation exists both inside the redux containers and the wrapper component, the navigation stack reference was different for them which is why methods such as navigate or goBack() were not working.
The solution was simple. Passing the navigation stack reference as screenProps to the wrapper component solves this issue.
<View style={{ flex: 1 }}>
<AppBarComponent />
<Tab screenProps={{ rootNav: this.props.navigation }} />
</View>