Ionic 5 Popover Position - ionic-framework

I am using ion-popover.
In the example in the docs, when you click the three dots at the top right, the popover is shown right next to the clicked button.
What would be a good way of reproducing this? Is there a built-in way of doing it?
Since I didn't find a way, I am trying to set the styles for the popover manually, but that doesnt work either.
My page.ts
const popover = await this.popoverController.create({
component: OptionsComponent,
cssClass: 'my-custom-class',
event: ev
});
return await popover.present();
My global.scss
.my-custom-class .popover-content {
position: absolute;
right: 0;
top: 0;
}
Am I doing something wrong? Is there a better way to approach this?

It's explained in the docs (emphasis mine) :
To present a popover, call the present method on a popover instance. In order to position the popover relative to the element clicked, a click event needs to be passed into the options of the the present method.
HTML
<div (click)="showPopover($event)">
<div>AAA</div>
</div>
In your class, pass the event as an argument to your method:
showPopover(event) {
const popover = await this.popoverController.create({
event,
component: OptionsComponent,
cssClass: 'my-custom-class', // optional
});
return await popover.present();
}
No need for extra CSS unless you want to style the content of your modal.

Related

adding additional layers to existing toggle visibility buttons

I have pieced together the following code using examples on Mapbox... https://codepen.io/charlie-enright/pen/ZExKOGB?editors=0010
What I want to do now is add another raster layer but have its visibility controlled by one of the existing buttons (the "geophys")button rather than to have to add an extra button.
Is it possible to have two separate raster layers controlled by the same toggle button with the script I have used? If so how will I go about doing this?
The code for the additional raster layer I want to add to be controlled by the geophys button is:
map.addSource('rudbaxton', {
'type': 'raster',
'url': 'mapbox://charlie-enright.chlrzasw'
});
map.addLayer({
'id': 'geophys1',
'source': 'rudbaxton',
'type': 'raster'
});
On top of this I would like the opacity control to adjust the opacity for the two raster layers controlled by the "geophys" button. is this possible to do as well?
Thanks,
The way the example you used is set up, the text in the button must exactly match the name of the layer you want to toggle.
This line gets the text from the button that was clicked:
const clickedLayer = this.textContent;
This line gets the current visibility of the layer with the same name const visibility = map.getLayoutProperty(clickedLayer, "visibility");`
To control two layers with one button click, you just need to add some logic to check for which button was clicked, and respond accordingly:
link.onclick = function (e) {
const clickedButtonText = this.textContent;
e.preventDefault();
e.stopPropagation();
if (clickedButtonText === "geophys") {
// assuming your two layers should always be on or off together, only check the current visibility of one of them
const visibility = map.getLayoutProperty("some-first-layer", "visibility");
// Toggle layer visibility by changing the layout object's visibility property.
if (visibility === "visible") {
map.setLayoutProperty("some-first-layer", "visibility", "none");
map.setLayoutProperty("some-second-layer", "visibility", "none");
this.className = "";
} else {
this.className = "active";
map.setLayoutProperty("some-first-layer", "visibility", "visible");
map.setLayoutProperty("some-second-layer", "visibility", "visible");
}
}
// repeat the above if statement for the other button(s)
};
As a bonus, this means you can use any text you want in the buttons, which might be a little more human-friendly than using the layer names!

View is not resized onKeyboardWillShow, but onKeyboardDidShow

When I click the keyboard and it opens op, the keyboard first finishes the animation. Then the view is resized. This causes for lag when the view scrolls to the field.
I have tried fixing it this way:
this.keyboard.onKeyboardWillShow().then(() => {
this.content.scrollToBottom();
});
But because the view is not yet resized, it does not scroll. How can I fix this? The app doesn't feel native at all without this.
The solution I found for this is as follows.
I created a wrapper within my <ion-content>:
<ion-content>
<div id="wrapper"></div>
</ion-content>
And styled it this way:
#wrapper{
height: 100%;
margin-bottom: 200px; /* it is best to get the keyboard height here from `onKeyboardWillShow()` */
}
Then in ts I did this:
this.keyboard.setResizeMode(KeyboardResizeMode.None);
this.keyboard.onKeyboardWillShow().subscribe(() => {
this.content.scrollToBottom();
});
this.keyboard.onKeyboardWillHide().subscribe(() => {
this.content.scrollToTop(500);
});
Works just like I want it. But I am still open to better answers.

How to Set Width of sap.m.MessagePopover?

The control sap.m.MessagePopover has an attribute _oPopover (containing sap.m.Popover inside).
Using this attribute, I could set the popover width:
messagePopover._oPopover.setContentWidth("450px");
However, as SAP attributes starting from _ should not be used, does anybody know a cleaner way?
As of UI5 version 1.46, a more flexible control sap.m.MessageView can be used instead of the old sap.m.MessagePopover.
There is no need to access internal properties or apply custom CSS style classes to manipulate the width as you can put MessageView anywhere you want (Still, Fiori Guideline recommends to use it only within a responsive popover or a dialog).
const popover = new ResponsivePopover({
contentWidth: "450px",
contentHeight: "450px",
content: [
/*myMessageView*/
],
});
// ...
popover.openBy(...);
Compared to MessagePopover, MessageView can group items and more.
Internally, MessagePopover uses MessageView too.
Another solution would be to use CSS class. However, there is a catch. As you can see from below generated DOM of the message popover, inline styling has been used :( .
Only way to override inline-style is by using !important in CSS which is again not recommended approach. However, considering inline CSS has been used, I would go with using !important keyword. Below is the working code:
XML Code ( for adding Class):
<MessagePopover id='myMP' class='myPopoverClass'>
<items>
<MessagePopoverItem title='Title' subTitle='SubTitle'></MessagePopoverItem>
</items>
</MessagePopover>
CSS:
.myPopoverClass {
width:100rem;
}
.myPopoverClass .sapMPopoverCont {
width:100% !important;
}
You can play around with how much width you need for message Popover.
EDIT: This is from the source code:
MessagePopover.prototype.init = function () {
var that = this;
var oPopupControl;
this._oResourceBundle = sap.ui.getCore().getLibraryResourceBundle("sap.m");
this._oPopover = new ResponsivePopover(this.getId() + "-messagePopover", {
showHeader: false,
contentWidth: "440px",
placement: this.getPlacement(),
showCloseButton: false,
modal: false,
afterOpen: function (oEvent) {
that.fireAfterOpen({openBy: oEvent.getParameter("openBy")});
},
afterClose: function (oEvent) {
that._navContainer.backToTop();
that.fireAfterClose({openBy: oEvent.getParameter("openBy")});
},
beforeOpen: function (oEvent) {
that.fireBeforeOpen({openBy: oEvent.getParameter("openBy")});
},
beforeClose: function (oEvent) {
that.fireBeforeClose({openBy: oEvent.getParameter("openBy")});
}
}).addStyleClass(CSS_CLASS);

What is the need of $scope.closeModal?

Here is my code..If I remove close modal function,there is no effect. If I click any where outside the modal, the modal closes. But I need this close modal function as I need to set a flag in it for further use. How can I proceed further?
$scope.$on('$ionicView.afterEnter', function() {
$scope.openModal();
}
$ionicModal.fromTemplateUrl("settings/settingsModal.html", {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal;
});
$scope.openModal = function(){
$scope.modal.show();
}
$scope.closeModal = function(){
$scope.modal.hide();
};
}
There are a two ways of implementing modal in Ionic. One way is to add separate template and the other is to add it on top of the regular HTML file, inside script tags. First thing we need to do is to connect our modal to our controller using angular dependency injection. Then we need to create modal. We will pass in scope and add animation to our modal.
After that we are creating functions for opening, closing, destroying modal and the last two functions are place where we can write code that will be triggered if modal is hidden or removed. If you don't want to trigger any functionality when modal is removed or hidden you can delete the last two functions.
Controller's Code:
.controller('MyController', function($scope, $ionicModal) {
$ionicModal.fromTemplateUrl('my-modal.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal;
});
$scope.openModal = function() {
$scope.modal.show();
};
$scope.closeModal = function() {
$scope.modal.hide();
};
//Cleanup the modal when we're done with it!
$scope.$on('$destroy', function() {
$scope.modal.remove();
});
// Execute action on hide modal
$scope.$on('modal.hidden', function() {
// Execute action
});
// Execute action on remove modal
$scope.$on('modal.removed', function() {
// Execute action
});
});
HTML Code :
<script id = "my-modal.html" type = "text/ng-template">
<ion-modal-view>
<ion-header-bar>
<h1 class = "title">Modal Title</h1>
</ion-header-bar>
<ion-content>
<button class = "button icon icon-left ion-ios-close-outline"
ng-click = "closeModal()">Close Modal</button>
</ion-content>
</ion-modal-view>
</script>
There are also other options for modal optimization. I already showed how to use scope and animation. The table below shows other options.
Modal options
The close modal function is meant for situations where you would like to close the modal manually. For example after a certain time it has been open or if something happens/the user does something for example presses a button.
There are ways of listening to when the modal is hidden/removed which will suit your situation and needs. For example:
// Execute action on hide modal
$scope.$on('modal.hidden', function() {
// Execute action
console.log('modal was hidden');
});
// Execute action on remove modal
$scope.$on('modal.removed', function() {
// Execute action
console.log('modal was removed');
});
With these you should be able to do what I understood you are wanting to do.
Straight from the documentation: http://ionicframework.com/docs/api/service/$ionicModal/

Add button to fancytree node to delete/remove that node?

I cannot find an example to do this anywhere, although I could have sworn I've seen one in the past.
I want to add a button to a node in fancytree so that either on hovering over that node (or maybe on selecting it) the button displays (a white x on a red circle, for example) and clicking it will delete/remove that node. At all other times the delete button should be hidden for the node.
I've been unable to find any kind of example where a custom link or button is added to a fancytree node though - maybe it's not possible to do or I'm just using the wrong search terms?
Edit: I found a way to add a clickable button by appending html to the title string:
title: component.name() + "<span class='deleteButton'><a href='#' data-bind='click: myfunction'><img src='../../Content/images/deleteIcon.png' /></a></span>",
And by adding some custom css to my site file:
span.fancytree-node span.deleteButton {
display: none;
}
span.fancytree-active span.deleteButton {
margin-left: 10px;
display: inline-block;
}
But this adds the button to the title text and is therefore subject to the highlighting of the title when active. It would be better if there was a way to add this to the node OUTSIDE of the title text. Is that possible Martin?
$("#tree").fancytree({
source: [...],
renderNode: function (event, data) {
var node = data.node;
var $nodeSpan = $(node.span);
// check if span of node already rendered
if (!$nodeSpan.data('rendered')) {
var deleteButton = $('<button type="button" class="btn">delete node</button>');
$nodeSpan.append(deleteButton);
deleteButton.hide();
$nodeSpan.hover(function () {
// mouse over
deleteButton.show();
}, function () {
// mouse out
deleteButton.hide();
})
// span rendered
$nodeSpan.data('rendered', true);
}
}
});
I normally use css ':after' for such cases (https://developer.mozilla.org/de/docs/Web/CSS/::after).
If that is not enough, you can always tweak the markup in the 'renderNode' event.