Performance issue with #angular/material autocomplete - angular8

I am using Angular/Material Autocomplete. While loading data to the Autocomplete getting serious performance issues,like the rendering takes around 30 seconds and it takes more than 10 seconds to become stable,data is loaded from the server, and the data received from the server is quite fast. To Over Come that issue i used cdkVirtualScroll, after scrolling down to end and clicking again the text box it's loading empty popup after scroll its loading values.
html
<mat-form-field>
<input type="text" placeholder="Pick one" aria-label="Number" matInput [matAutocomplete]="auto">
<mat-autocomplete #auto="matAutocomplete" (opened)="panelOpened()">
<cdk-virtual-scroll-viewport itemSize="48" style="height: 240px" minBufferPx="96" maxBufferPx="144">
<mat-option *cdkVirtualFor="let option of options" [value]="option">
{{option}}
</mat-option>
</cdk-virtual-scroll-viewport>
</mat-autocomplete>
</mat-form-field>
TS
export class AppComponent {
options: string[] = [];
#ViewChild(CdkVirtualScrollViewport, {static: true}) viewport: CdkVirtualScrollViewport;
constructor() {
for (let i = 0; i < 10000; i++) {
this.options.push("#"+i);
}
}
panelOpened() {
if (this.viewport) {
this.viewport.checkViewportSize();
}
}
}
Check the ex: https://stackblitz.com/edit/angular-7vcohv?file=src%2Fapp%2Fapp.component.html

I'm not sure how many options the mat-autocomplete is targeted to support, but my suggestions to improve the performance are:
Fill the autocomplete only after the user typed at least 2 characters.
Implement a server-side search and fill the auto-complete options after you got smaller amount of options.
If you think this is an issue with the mat-autocomplete component, you can open an issue in the #angular/material repository.

I believe the core problem is that you are using viewChild to reference the viewPort but there are multiple viewports. The AutoComplete setup in the html had similar issues with the setup.
The below StackBlitz seems to be working. I would think the way you had it would work though if you only had one auto complete on the screen.
https://stackblitz.com/edit/angular-rzqjz8?file=src%2Fapp%2Fapp.component.ts

Related

how do _mobile_* divs gets populated with html data when viewing a prestashop page in mobile

I want to understand how prestashop works regarding mobile displays.
I noticed in the used template, the header.tpl file contains the following html divs for mobile:
<div class="float-xs-right" id="_mobile_language_selector"></div>
<div class="float-xs-right" id="_mobile_user_info"></div>
<div class="float-xs-right" id="_mobile_cart"></div>
<div class="float-xs-right" id="_mobile_currency_selector"></div>
I also noticed that once I remove any of the components (for example the shopping card) from theme.yml:
global_settings:
configuration:
PS_IMAGE_QUALITY: png
modules:
to_enable:
- ps_linklist
hooks:
modules_to_hook:
displayNav1:
- ps_contactinfo
- tuxinmodaccessibility
displayNav2:
- ps_languageselector
- ps_currencyselector
- ps_customersignin
REMOVE THIS LINE ->>> - ps_shoppingcart
displayTop:
then the cart component is not displayed in the navbar. so the mobile and deskop version required this configuration to be set.
I noticed also that for each component besides having main div with _mobile_ prefix, there are also divs with _desktop_ prefix.
I'm trying to find out how to properly add my accessibility component to the navbar and that it will also be displayed on mobile.
so far it displays only on desktop and not on mobile so I was guessing that I need to add something like
<div class="float-xs-right" id="_mobile_tuxinmodaccessibility"></div>
no idea how to implement it properly.
I don't quite understand how for example, how this process works for mobile_cart div while the module name is ps_shoppingcart.
any information regarding the issue would be greatly.
You need to checkout themes/classic/_dev/js/responsive.js file.
The answer is in the theme.js file.
Script moves contents between desktop and mobile HTML elements in DOM. Every HTML element with ID that starts with id="_mobile_" gets content from corresponding desktop variation that starts with id="_desktop_" (if you inspect DOM in mobile view you'll notice that desktop elements got empty).
function o() {
u.default.responsive.mobile ? (0, s.default)("*[id^='_desktop_']").each(function(t, e) {
var n = (0, s.default)("#" + e.id.replace("_desktop_", "_mobile_"));
n.length && r((0, s.default)(e), n)
}) : (0, s.default)("*[id^='_mobile_']").each(function(t, e) {
var n = (0, s.default)("#" + e.id.replace("_mobile_", "_desktop_"));
n.length && r((0, s.default)(e), n)
}), u.default.emit("responsive update", {
mobile: u.default.responsive.mobile
})
}

Adding a dollar sign to an ion-input

I currently have this:
<ion-item>
<ion-label sale floating>Sale Price< /ion-label>
<ion-input type="number" clearInput placeholder="$">< /ion-input>
</ion-item>
What I'd really like, is to add a $ to the front of any input so that when a user types in a number it appears with a dollar sign next to, or inside it. I'm finding this impossible currently. In a standard input I'd put the before or in a span or label preceding the input in order to make it work.
However with the Ion-item in an Ion-list it seems that adding a span breaks it, and I'm not sure how, using scss, to add it after. Head exploding stuff.
I've tried:
ion-label[sale]:before {
content: "$";
}
On a whim, in hopes that would work. It does not.
Anyone experience this before and have a solution?
Thanks
Interesting question. The following should work because in practice I believe an <ion-input> gets translated to a standard HTML5 input in the DOM.
input[type=number]:before {
content: "$";
}
This is a great question and #Lightbeard's answer should be sufficient for most use cases. I want to share an alternative solution for this using Angular Pipes
#Pipe({name: 'roundDollars'})
export class RoundDollarsPipe implements PipeTransform {
transform(value: any) {
return _.isNumber(value) ? "$"+Math.round(value) : 'Price Error';
}
}
#Pipe({name: 'roundCents'})
export class RoundCentsPipe implements PipeTransform {
transform(value: any) {
return _.isNumber(value) ? "$"+value.toFixed(2) : 'Price Error';
}
}
In the template, you can implement like this if you are using a form control:
<ion-item>
<ion-label sale floating>Sale Price< /ion-label>
<ion-input type="number" formControlName="salePrice" value="{{ formControl.get('salePrice').value | roundDollars }}">< /ion-input>
</ion-item>
This has the disadvantage that it will pre-pend a '$' to the actual value submitted in your form. I store my data as a number rather than a string, so I put the '$' character on a disabled input lower in the form to display the total of several inputs.
This means the actual input where the user is typing does not show a '$' but that appears lower in the form.
My production template looks like this:
<ion-item>
<ion-label floating>Monthly Total:</ion-label>
<ion-input [disabled]="true" class="invisible" type="number" formControlName="monthlyFee"></ion-input>
</ion-item>
<p class="offset-y-32">{{ detailsForm.get('monthlyFee').value | roundDollars }}</p>
My .invisible class simply sets opacity: 0 so you can still use the line from the ion-item, while .offset-y-32 moves the <p> text up 32 pixels.
Here is a screen shot of the form

How to keyboard navigate to reCAPTCHA in a modal dialog?

I need to open Google's latest reCAPTCHA widget in a popup (modal) dialog, a Dojo Dialog in my case, and I've got that working fine, but I just realized that the user cannot keyboard navigate to it.
When the reCAPTCHA widget is displayed in the main view, not a modal dialog, then of course the user can easily keyboard navigate to it.
Has anyone found a way to set focus on the reCAPTCHA widget so that the user can access it without a mouse when the reCAPTCHA is in a Dojo Dialog?
I did see that reCAPTCHA is generated within an <iframe>. Is that part of the hurdle - that keyboard navigation can't reach content within an iframe? I've even tried to call document.getElementById("recaptcha-anchor") since I saw that that's the id of the <span> that holds the "checkbox" - but that is returning null. How to reach an element within an iframe?
I have a jsfiddle example available for demonstration at
https://jsfiddle.net/gregorco/xqs8w5pm/5/
<script>
var onloadCaptchaCallback = function() {
console.log("jsfiddle: rendering captcha");
globalRecaptchaWidgetId = grecaptcha.render('captchaDiv', {
'sitekey' : '6LcgSAMTAAAAACc2C7rc6HB9ZmEX4SyB0bbAJvTG',
'callback' : verifyCaptchaCallback,
'tabindex' : 2
});
grecaptcha.reset();
}
var verifyCaptchaCallback = function(g_recaptcha_response) {
console.log("Response validated. Not a robot.");
};
</script>
<script src='https://www.google.com/recaptcha/api.js?onload=onloadCaptchaCallback&render=explicit' async defer></script>
<div id="testDiv">
<button type="dojo/form/Button" onClick="captchaPopup.show();">Open reCAPTCHA</button>
</div>
<div data-dojo-type="dijit/Dialog" data-dojo-id="captchaPopup" title="Human Verification" style="width:350px;">
Cannot keyboard navigate to the checkbox!
<table>
<tr>
<td>
<div id="captchaDiv"></div><br/>
</td>
</tr>
</table>
</div>
Give this fiddle a try. Normally Dijit dialogs don't work too well with iframes in them because it doesn't know how to parse the content inside an iframe. In this case, we can use some of Dojo's functions to work around it. One notable thing to point out is that I've disabled autofocus of the Dijit Dialog so that it won't automatically focus the closeNode inside the dialog.
After the dialog loads, tab>space will select the captcha.
This may help others facing similar issue, but with Bootstrap modal dialog. I found the following solution on GitHub. Add the following Javascript to override Bootstrap:
Bootstrap 3x
$.fn.modal.Constructor.prototype.enforceFocus = function () { };
Bootstrap 4x
$.fn.modal.Constructor.prototype._enforceFocus = function () { };

Am I doing this data-menu-top properly? Do I need to change my layout entirely to get this to work?

I'm using data-menu-top on this page because everything is fixed and uses Skrollr to animate the different sections into view. The reason everything is fixed is so that I could do full-page SVGs that cover the height of the page (if you think there's a better way to do this, I would love to be enlightened).
Here's a link to the project development page: http://pman.mindevo.com
The button that appears on the first section has data-menu-top="10300", and this works great on Chrome, but when I try to view it in Firefox (33.0) the link doesn't do anything at all.
I am initializing using this code:
<script type="text/javascript">
setTimeout(function() {
var s = skrollr.init({
});
skrollr.menu.init(s, {
easing: 'quadratic',
duration: function(currentTop, targetTop) {
return 1500;
}
});
}, 1000);
</script>
Am I properly using data-menu-top? Is this a bug I'm not aware of using fixed layouts that are hidden using height?
Do I need to change the layout somehow to accomplish what I want and have it work in Firefox?
So the problem with Firefox was the way that it handles <button> linking. Here's the way the button was in the HTML:
<button class="buy buypotato">
<a data-menu-top="10300" href="#potatoPurchase1" class="purchase-options first-popup-link">
<svg ....etc></svg>
</button>
In Firefox it wasn't doing anything upon clicking, and got me thinking perhaps I'm using "button" HTML element incorrectly. Anyways, changing it to a div like so:
<div class="buy buypotato">
<a data-menu-top="10300" href="#potatoPurchase1" class="purchase-options first-popup-link">
<svg ....etc></svg>
</div>
That allowed Firefox to utilize Skrollr-menu to scroll to where I needed it to.
There might be a better way to do the layout on this, I'm still experimenting.

jQuery: Select elements with Incrementing ID names?

and thanks in advance for your help!
Here's my situation: I have a set of divs whose IDs have an incrementing number applied to the end of the name using PHP. Each of these divs are added dynamically with PHP (They are a series of FAQ questions with a hidden div container with the answers, that slide down when the question is clicked.) [Live Example][1]
There is no limit to the number of questions that appear on the page, because this is being used for a Wordpress theme and my client wants to add new questions as they go along.
Here's an example of the structure for each FAQ question using the PHP:
<?php var $faqnum = 0; $faqnum++; ?>
<div id="faqwrap<?php $faqnum; ?>">
<h4>What data is shared?</h4>
<div id="faqbox<?php $faqnum; ?>" class="slidebox">
<p>Data sharing is defined by the type of service:</p>
<ul class="list">
<li>Third-party access to data (Enhanced Services only is strictly controlled and determined by the SDA)</li>
<li>All members must participate in points of contact and conjunction assessment but can choose whether to participate in other services</li>
<li>Participation in a service requires the member to provide associated data<br />
</ul>
</div>
</div>
Now this is what I have currently in jQuery, and it works, but only if I add a new one every time my client wants to add a new question.
$(document).ready(function() {
$('.slidebox*').hide();
// toggles the slidebox on clicking the noted link
$("#faqwrap1 a:not(div.slidebox a)").click(function() {
$("#faqbox1.slidebox").slideToggle('normal');
$('div.slidebox:not(#faqbox1)').slideUp('normal');
return false;
});
});
I thought of maybe doing something with a declared variable, like this:
for (var x = 0; x < 100; x++;) {
$('#[id^=faqwrap]'+ x 'a:not(div.slidebox a)')...
}
I hope this is clear enough for you! Again, I thank you in advance. :)
The best way to handle this is to not use the IDs, but use classes for the outer element. So your PHP would be altered like this:
<?php var $faqnum = 0; $faqnum++; ?>
<div id="faqwrap<?php $faqnum; ?>" class="question">
<h4>What data is shared?</h4>
<div id="faqbox<?php $faqnum; ?>" class="slidebox">
<p>Data sharing is defined by the type of service:</p>
<ul class="list">
<li>Third-party access to data (Enhanced Services only is strictly controlled and determined by the SDA)</li>
<li>All members must participate in points of contact and conjunction assessment but can choose whether to participate in other services</li>
<li>Participation in a service requires the member to provide associated data<br />
</ul>
</div>
</div>
Your JQuery would be rewritten with the selector for the class "question".
$(document).ready(function() {
$('.slidebox*').hide();
// toggles the slidebox on clicking the noted link
$(".question a:not(div.slidebox a)").click(function() {
/* close everything first */
$('div.slidebox').slideUp('normal');
/* selects and opens the the slidebox inside the div */
$(".slidebox", this).slideToggle('normal');
return false;
});
});
This will get you the effect you are looking for. The key differences in the JQuery is the way you get the slidebox inside the question that got clicked. I'm using the scoped selection $(".slidebox", this) to get just the slidebox inside the clicked ".question" element.
The subtle visual difference is that the slideUp() happens before the slideToggle(). This will essentially close any open queries before it opens the desired one. If you keep your animations fast, this will be more than fine. The advantage of this approach is that you don't have to worry about the count of questions on a page, and the selectors are most likely more optimized than the for loop.
Edit
I adjusted the PHP code to use a class for "slidetoggle" instead of an id. It's technically an HTML error to have multiple IDs that are the same. It can throw off some assistive technologies for people with dissabilities. I'm assuming that section of code was repeated several times on the page.
Without changing your current markup, this would work:
// toggles the slidebox on clicking the noted link
$("div[id=^faqwrap]").each(function () {
var $faqwrap= $(this);
$faqwrap.find("h4 > a").click(function () {
var $currentSlidebox = $faqwrap.children(".slidebox");
$currentSlidebox.slideToggle('normal');
$('div.slidebox').not($currentSlidebox).slideUp('normal');
return false;
});
});
Maybe you can find a few suggestions in the above code that help you.
Like #Berin, I'd also recommend giving a separate CSS class to the outer DIV and using that as a selector, instead of $("div[id=^faqwrap]").