center paper-dialog upon opening - material-design

I'm trying to create a simple login window using Angular 2 and Polymer 1.0.2.
It's basically a paper-dialog (login window) with content. The dialog however is not positioned in the center of the screen, see this bugreport Dialog not centered until window resize #36:
The issue suggests calling the notifyResize() method on the paper-dialog. But I've no clue as how to refer to the paper-dialog instance in my angular 2/polymer class.
Somehow the import {PaperDialog} is not resolved, but looking in the paper-dialog.html makes me wondering if such an import is possible at all.
import {Component, View} from 'angular2/angular2';
import {PaperDialog} from 'bower_components/paper-dialog/paper-dialog';
#Component({
template: `
<paper-dialog open>
...
</paper-dialog>
`,
selector: 'login-window',
directives : [PaperDialog]
})
export class LoginWindow {
email: string;
password: string;
constructor(){
this.email = '';
this.password = '';
// Where and how to call the PaperDialog child's notifyResize() method
}
}
Note that I'm not opening the dialog programmatically (fix described here).
This solution uses the /deep/ selector that is deprecated.
And it shouldn't be fixed by applying some css, should it?

Instead of having my code fix the paper-dialog behaviour's code, it's way better to fix the problem itself.
Add the call this.notifyResize(); to the _onIronOverlayOpened method in the paper-dialog-behavior.html source.
...
_onIronOverlayOpened: function() {
if (this.modal) {
document.body.addEventListener('focus', this._boundOnFocus, true);
this.backdropElement.addEventListener('click', this._boundOnBackdropClick);
}
this.notifyResize(); // Added this line
},
...
Although this resolves my simple paper-dialog center problem, I can not oversee consequences for other elements and code yet.

You could also fix it in the following manner:
Within an angular2 Component (that wraps the paper-dialog element) you could so something like this (Typescript example):
interface PaperDialog extends HTMLElement {
notifyResize(): void;
}
#Component({
selector: 'login'
})
#View({
templateUrl: 'app/components/ts-login-window/ts-login-window.html'
})
export class LoginWindow {
email: string;
password: string;
dialogWindow: PaperDialog;
private bindIronOverlayOpened: EventListenerObject;
constructor(elementRef: ElementRef){
this.dialogWindow = elementRef.nativeElement.children[0];
this.bindIronOverlayOpened = this.ironOverlayOpened.bind(this);
this.dialogWindow.addEventListener('iron-overlay-opened', this.bindIronOverlayOpened);
}
ironOverlayOpened(event: Event) {
this.dialogWindow.notifyResize();
}
onDestroy() {
this.dialogWindow.removeEventListener('iron-overlay-opened', this.bindIronOverlayOpened);
}
}
Once the paper-dialog has been opened (by the event iron-overlay-opened) you could trigger the notifyResize() event on the dialog box. This in turn fixes the alignment problem.

Related

Can't get html element using js file in SPFX

I am trying to build dynamic content from a SharePoint list using SPFX. I'd like to use jQuery to build an accordion view of the data. The issue is that I can't even seem to get the element once the page is rendered.
In my code I am requiring a file called ota.js with the following code:
console.log('Start');
function otaExpand(){
console.log('otaExpand Function Called');
let spListContainer = document.getElementById('spListContainer');
console.log(spListContainer);
}
window.addEventListener("load", otaExpand());
In my ts file this is my render method:
public render(): void {
this.domElement.innerHTML = `
<div>
<div id="spListContainer">TEST</div>
</div>
`;
//this._renderListAsync();
//($('.accordion', this.domElement) as any).accordion();
}
When I review the console, I get my messages, but the element itself comes back as null.
console.log
I am using SharePoint 2019 on premise with the following configuration.
+-- #microsoft/generator-sharepoint#1.10.0
+-- gulp-cli#2.3.0
`-- yo#2.0.6
node --version
v8.17.0
I should also mention I am using TypeScript with no JavaScript framework.
Does anyone know why I can't access this element from my js file?
Thanks!
My overall goal is to call list data and apply an accordion style to it (https://jqueryui.com/accordion), but I can't even get passed capturing the element to change it.
I've tried calling my code from a js file as well as trying to put the code directly in the html. Neither worked.
OK, I finally figured out what I was doing wrong. I was calling my jQuery in the render() method rather than in _renderList where this.domElement actually makes sense.
Here's my code in case anyone wants to avoid the pain I put myself through. This allows you to specify a list in the site and you just need to add the fields you want to display.
import { Version } from '#microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneChoiceGroupOption,
IPropertyPaneConfiguration,
PropertyPaneChoiceGroup,
PropertyPaneCustomField,
PropertyPaneTextField
} from '#microsoft/sp-webpart-base';
import { escape } from '#microsoft/sp-lodash-subset';
import styles from './GetSpListItemsWebPart.module.scss';
import * as strings from 'GetSpListItemsWebPartStrings';
import {
SPHttpClient,
SPHttpClientResponse
} from '#microsoft/sp-http';
import * as jQuery from 'jquery';
import 'jqueryui';
import { SPComponentLoader } from '#microsoft/sp-loader';
import PropertyPane from '#microsoft/sp-webpart-base/lib/propertyPane/propertyPane/PropertyPane';
export interface IGetSpListItemsWebPartProps {
title: string;
description: string;
listField: string;
}
export interface ISPLists {
value: ISPList[];
}
export interface ISPList {
ID: string;
Title: string;
Website: {
Description : string,
Url : string
};
Description : string;
}
export default class GetSpListItemsWebPart extends BaseClientSideWebPart<IGetSpListItemsWebPartProps> {
private _getListData(): Promise<ISPLists> {
return this.context.spHttpClient.get(this.context.pageContext.web.absoluteUrl + "/_api/web/lists/GetByTitle('" + this.properties.listField + "')/Items",SPHttpClient.configurations.v1)
.then((response: SPHttpClientResponse) => {
return response.json();
});
}
private _renderListAsync(): void {
this._getListData()
.then((response) => {
this._renderList(response.value);
})
.catch(() => {});
}
private _renderList(items: ISPList[]): void {
let listData = `
<h1>${this.properties.title}</h1>
<h2>${this.properties.description}</h2>
<div class="accordion">
`;
items.forEach((item: ISPList) => {
let Description : string;
item.Description ? Description = item.Description : Description = "";
listData += `
<h3> ${item.Title}</h3>
<div>
<table>
<tr>
<td>OTA URL</td>
<td>${item.Website.Description}</td>
</tr>
<tr>
<td>Description</td>
<td>${Description}</td>
</tr>
</table>
</div>
`;
});
listData += '</div>';
this.domElement.innerHTML = listData;
const accordionOptions: JQueryUI.AccordionOptions = {
animate: true,
collapsible: true,
icons: {
header: 'ui-icon-circle-arrow-e',
activeHeader: 'ui-icon-circle-arrow-s'
}
};
jQuery('.accordion', this.domElement).accordion(accordionOptions);
}
public render(): void {
this._renderListAsync();
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('title',{
label: strings.TitleFieldLabel
}),
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
}),
PropertyPaneTextField('listField', {
label: strings.ListFieldLabel
})
]
}
]
}
]
};
}
public constructor() {
super();
SPComponentLoader.loadCss('//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css');
}
}
Your code from the "ota.js" file is probably called before your HTML is initialized (i.e. before the "render()" function is executed). To make sure this is the case, you could add log to the "render()" function to see when it's called.
In other words, "window.load" event happens long before "render()" function is called. This is how web parts are loaded - dynamically after full load of the page. Or "window.load" does not happen at all - web parts may be loaded by the user when using the page designer, i.e. without page reload.
To fix the issue, you should get the element after it's created, i.e. after the "render()" function creates the element you are trying to get.

ionViewWillEnter doesn't work on entering the page

As the title suggests, my function doesn't trigger once I access the page itself. I have a button that is supposed to redirect me to another page, yet the page itself doesn't load nor the function. The code at the moment looks like this:
import {Component} from "#angular/core";
import {NavController, AlertController} from "ionic-angular";
#Component({
selector: 'page-patch-notes',
templateUrl: 'patch-notes.html'
})
export class PatchNotes {
constructor(private nav: NavController, private alertCtrl: AlertController) {
}
ionViewWillEnter() {
let alert = this.alertCtrl.create({
title: 'Patch Notes',
message: "Introduceti adresa de email pentru a va trimite un link de resetare parola.",
buttons: [
{
text: 'Anulati',
handler: data => {
console.log('Cancel clicked');
}
},
{
text: 'Ok',
handler: data => {
console.log('Ok clicked');
}
},
]
});
alert.present();
}
}
It's likely that the alert controller is created and displayed before the page is even loaded. Ionic lifecycles states that his hook fires
between the "Begin Page Transition" and "Page Fully Transitioned" stages.
I have had a lot of success tying this kind of logic to ngOnInit. You will need to update your class def to export class PatchNotes implements OnInit. The fact that this wasn't done suggests that fransicso neto's suggestion may be accurate as well. If you aren't already, it's always a good idea to generate pages using the Ionic CLI.

can get data out of http request

i have a problem receiving data to variable.
this works:
this.http.get(some_url).subscribe(
data => {
console.log(JSON.parse(data['_body']))
});
but if i try to get the value to a variable instead of printing it, it does not work:
this.http.get(some_url).subscribe(
data => {
this.var = JSON.parse(data['_body'])
});
i need your help, i tried everything.
Thanks!
This doesn't seem to be an angular 2 issue. Can you try the following steps to debug?
Check the console (F12) for any errors while making the GET call
Print and store the returned the data in the variable
Are there any lines above the variable assignment line that you have not posted?
I've created a plunker where you can see the variable assignment in action
https://embed.plnkr.co/MEHTZ92HgZhEbUZlBELQ/
the app.component.ts is as follows
import { Component } from '#angular/core';
import { Http } from '#angular/http';
#Component({
selector: 'my-app',
template: `
<div>
<p>Content: {{ content }}</p>
</div>
`,
})
export class AppComponent {
content: string;
constructor(private http: Http) {
this.http.get('https://jsonplaceholder.typicode.com/posts/1').subscribe(
data => {
this.content = data
});
}
}
And the result I get is
Content: Response with status: 200 OK for URL: https://jsonplaceholder.typicode.com/posts/1
Of course, one shouldn't use the constructor for network calls and similar heavy stuff and should use ngOnInit instead, but just for the sake of example I've made the call in the constructor

How to show a Kendo UI for Angular combobox open and use an *ngIf directive in it

ComboBoxComponent provides the method toggle that toggles the visibility of the comboBox popup. I want to display comboBox already opened. I have the following implementation:
datalist.component.html
<kendo-combobox #attributecombobox></kendo-combobox>
datalist.component.cs
#Component({
templateUrl: './datalist.component.html'
})
export class DatalistComponent implements OnInit {
#ViewChild('attributecombobox') public attributeCombobox: ComboBoxComponent;
}
I've tried setting the constructor:
constructor() {
this.attributeCombobox.toggle(true);
}
Doesn't work. I also tried the OnInit lifecycle hook:
ngOnInit() {
this.attributeCombobox.toggle(true);
}
It also does not work.
What is the right approach for this? Thanks in advance.
Update 1
Sorry, I didn't disclose all the code. The ComboBox actually has a *ngIf:
datalist.component.html
<kendo-combobox #attributecombobox *ngIf="setAttribute"></kendo-combobox>
datalist.component.cs
#Component({
templateUrl: './datalist.component.html'
})
export class DatalistComponent implements OnInit {
#ViewChild('attributecombobox') public attributeCombobox: ComboBoxComponent;
setAttribute = true;
ngOnInit() {
this.attributeCombobox.toggle(true);
}
}
So I think that I found an issue with kendo-combobox elements using *ngIf as you can see in this plunker that I forked from George K plunker (thanks George).
Update 2
I submitted an issue which was classified as a bug here.
The earliest possible place to open the component is in the ngOnInit (your second attempt). Calling the toggle method works just fine for me:
ngOnInit() {
this.combo.toggle();
}
Here is a runnable plunker:
http://plnkr.co/edit/ssbftD6hg3f7LM86CIPD?p=preview
Update
Indeed, the component will not be available in ngOnInit hook if a structure directive like ngIf is applied. Basically, this will desugar to
<ng-template [ngIf]="show">....combobox here... </ng-template>
As you've probably already noticed, the component inside the template will not be there on first init. The solution is to use a hook that will be called later in the component initialization, like AfterViewInit:
ngAfterViewInit() {
setTimeout(() => {
this.combo.toggle();
});
}
The updated plunkr demo can be found here - http://plnkr.co/edit/quLb3oeiVRJfqACqGKEK?p=preview

Disable the side menu's swipe to open gesture for Login page (or any page) in Ionic 2

I'm new to Ionic 2 & Angular2 and I have downloaded a new Ionic template with the following command
Ionic start appname sidemenu --v2 --ts
For this particular solution I have added a login page to validate a user. Once the validation succeeds the user will be navigated to the menu page which uses the side menu.
As the solution is based on the sidemenu template, the side menu is showing on the login page whenever the user swipes left.
So can somebody please guide me to rectify this mistake and stop the side menu from showing when the view is swiped.
My code
App.ts file
import {App, IonicApp, Platform,MenuController} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HelloIonicPage} from './pages/hello-ionic/hello-ionic';
import {ListPage} from './pages/list/list';
import {HomePage} from './pages/home/home';
#App({
templateUrl: 'build/app.html',
config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
class MyApp {
// make HelloIonicPage the root (or first) page
rootPage: any = HomePage;
pages: Array<{title: string, component: any}>;
constructor(
private app: IonicApp,
private platform: Platform,
private menu: MenuController
) {
this.initializeApp();
// set our app's pages
this.pages = [
{ title: 'Hello Ionic', component: HelloIonicPage },
{ title: 'My First List', component: ListPage }
];
}
initializeApp() {
this.platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
StatusBar.styleDefault();
});
}
openPage(page) {
// close the menu when clicking a link from the menu
this.menu.close();
// navigate to the new page if it is not the current page
let nav = this.app.getComponent('nav');
nav.setRoot(page.component);
}
}
app.html file
<ion-menu side-menu-content drag-content="false" [content]="content">
<ion-toolbar>
<ion-title>Pages</ion-title>
</ion-toolbar>
<ion-content>
<ion-list>
<button ion-item *ngFor="#p of pages" (click)="openPage(p)">
{{p.title}}
</button>
</ion-list>
</ion-content>
</ion-menu>
<ion-nav id="nav" [root]="rootPage" #content swipe-back-enabled="false"></ion-nav>
Homepage.ts file (login page in this case).
import {Page, Events,Alert,NavController,Loading,Toast,Storage,LocalStorage,SqlStorage} from 'ionic-angular';
import { FORM_DIRECTIVES, FormBuilder, ControlGroup, Validators, AbstractControl } from 'angular2/common';
import {HelloIonicPage} from '../hello-ionic/hello-ionic';
import {NgZone} from 'angular2/core';
#Page({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {
public Uname :string;
public usrvalid:boolean;
public usrpwd :boolean;
public usrpwdlength:boolean;
public usrvalidlength:boolean;
public isUnchanged:boolean;
public usrpwdzero:boolean;
public usrvaliddigits:boolean;
rootpage:any;
public Upwd:string;
constructor(public nav:NavController) {
this.nav=nav;
this.isUnchanged=true;
var mediumRegex = new RegExp("^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})");
// rootPage: any = HomePage;
}
}
I think the drag-content directive is used in ionic 1, for Ionic 2 what you can do is disable it from within your page class file.
You can do this by importing the MenuController provider from ionic-angular and then call the .swipeEnable(shouldEnableBoolean, menuId) method to disable the swipe gesture within your page's class (this is also documented here).
Your login controller should be something like this...
import {Page, MenuController} from 'ionic-angular';
#Page({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage {
constructor(public menu: MenuController) {
this.menu.swipeEnable(false);
}
}
If you have multiple menus and each one has an id then you can target a specific menu like this...
this.menu.swipeEnable(false, `menuId`);
You can disable/enable sidemenu at any time at any page by calling
$ionicSideMenuDelegate.canDragContent(false)
e.g:
angular.module('ABC').controller('xyzCtrl', function($scope, $ionicSideMenuDelegate) {
$ionicSideMenuDelegate.canDragContent(false)
});

Resources