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
Related
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
Is it possible to dynamically define the type of an element inside a custom components template at runtime?
I'd like to avoid duplication of the inner contents of the button and a element in the following example:
<template>
<button if.bind="!isLinkBtn">
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</button>
<a if.bind="isLinkBtn">
<!--
The content is a 1:1 duplicate of the button above which should be prevented
somehow in order to keep the view DRY
-->
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</a>
</template>
Is it possible to write something like this:
<template>
<!--
The type of element should be defined at runtime and can be a standard HTML "button"
or an anchor "a"
-->
<element type.bind="${isLinkBtn ? 'a' : 'button'}">
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</element>
</template>
I'm aware of dynamic composition with <compose view="${widget.type}-view.html"></compose> but as far as I know, this won't allow me to create default HTML elements but only custom components, correct?
I've asked this question on the Aurelia Gitter where Erik Lieben suggested to use a #processContent(function) decorator, replace the content within the given function and return true to let Aurelia process it.
Unfortunately I don't know how to actually apply those instructions and am hoping for some alternative approaches here or some details about how to actually accomplish this.
Edit
I've created a corresponding feature request. Even though possible solutions have been provided, I'd love to see some simpler way to solve this ;)
When you want to reuse HTML snippets, use compose. Doing so does not create a new custom element. It simply includes the HTML at the location of each compose element. As such, the view-model for the included HTML is the same as for the element into which it is composed.
Take a look at this GistRun: https://gist.run/?id=36cf2435d39910ff709de05e5e1bedaf
custom-link.html
<template>
<button if.bind="!isLinkBtn">
<compose view="./custom-link-icon-and-text.html"></compose>
</button>
<a if.bind="isLinkBtn" href="#">
<compose view="./custom-link-icon-and-text.html"></compose>
</a>
</template>
custom-link.js
import {bindable} from 'aurelia-framework';
export class CustomLink {
#bindable() contentText;
#bindable() icon;
#bindable() isLinkBtn;
}
custom-link-icon-and-text.html
<template>
<span class="btn-icon">${icon}</span>
<span class="btn-text">${contentText}</span>
</template>
consumer.html
<template>
<require from="./custom-link"></require>
<custom-link content-text="Here is a button"></custom-link>
<custom-link is-link-btn.bind="true" content-text="Here is a link"></custom-link>
</template>
You may want to split these into separate elements, like <custom-button> and <custom-link> instead of controlling their presentation using an is-link-btn attribute. You can use the same technique to reuse common HTML parts and composition with decorators to reuse the common code.
See this GistRun: https://gist.run/?id=e9572ad27cb61f16c529fb9425107a10
Response to your "less verbose" comment
You can get it down to one file and avoid compose using the techniques in the above GistRun and the inlineView decorator:
See this GistRun: https://gist.run/?id=4e325771c63d752ef1712c6d949313ce
All you would need is this one file:
custom-links.js
import {bindable, inlineView} from 'aurelia-framework';
function customLinkElement() {
return function(target) {
bindable('contentText')(target);
bindable('icon')(target);
}
}
const tagTypes = {button: 'button', link: 'a'};
#inlineView(viewHtml(tagTypes.button))
#customLinkElement()
export class CustomButton {
}
#inlineView(viewHtml(tagTypes.link))
#customLinkElement()
export class CustomLink {
}
function viewHtml(tagType) {
let result = `
<template>
<${tagType}${tagType === tagTypes.link ? ' href="#"' : ''}>
<span class="btn-icon">\${icon}</span>
<span class="btn-text">\${contentText}</span>
</${tagType}>
</template>
`;
return result;
}
Sorry, I was doing 2 things at once while looking at gitter, which I am not good at apparently :-)
For the thing you wanted to accomplish in the end, could this also work?
I am not an a11y expert or have a lot of knowledge on that area, but from what I understand, this will accomplish what you want. The browser will look at the role attribute and handle it as a link or button and ignores the actual element type itself / won't care if it is button or anchor it will act as if it is of the type defined in role.
Then you can style it like a button or link tag with css.
<a role.bind="type"><span>x</span><span>y</span></a>
where type is either link or button, see this: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_link_role
So I am using a component framework for Vue2 named vuetify and I am having a problem regarding the class of the input textbox.
So in the textbox component, the code is:
<template lang="pug">
div(
class="input-group"
v-bind:class="classes"
)
label(
v-bind:for="id"
v-html="label"
)
input(
v-bind:id="id"
v-bind:name="name"
v-bind:placeholder="placeholder"
v-bind:required="required"
v-bind:type="type"
v-bind:value="inputValue"
ref="input"
)
</template>
Now I want to add a class in the text input because I want it to be a date and I am using the flatpickr plugin. The name of the class is flatpickr. So the way I use the component is like this:
<v-text-input id="someID" label="SomeLabel" v-model="someModel"></v-text-input>
And if I try to add the class random I do it like this:
<v-text-input id="someID" label="SomeLabel" v-model="someModel" class="flatpickr"></v-text-input>
The code isn't working because the class goes to the div. I checked the code generated in the elements of chrome and it looks like this:
<div class="input-group flatpickr" data-v-3eb87e8e="">
<label for="SomeLabel">SomeLabel</label>
<input id="someID" type="text">
</div>
As you can see, the class goes to the class of the div. What's the best way/trick to add a class in the text input? Any help would be much appreciated.
You can apply the same CSS property, by giving path of your input like following:
.random input{
height: etc;
}
Edit
Given that you are also getting id of the input box, to apply the class on specific input, you can also use the id:
.random #someid{
height: etc;
}
to add the class into <div> tag is the design of Vue, the class added into components will be rendered into the top level of the element in the template.
https://v2.vuejs.org/v2/guide/class-and-style.html#With-Components
If you want to apply the css to <input>, you can simply do:
.random input {
/* your css style */
}
If you want to use flatpickr with vuejs, you can try to use this vue-flatpickr component, https://github.com/jrainlau/vue-flatpickr
I need some help in figuring out the best way to create a customAttribute that will allow for an easy edit-toggle. Here is what I'm looking for:
<tr toggle-edit>
<td edit-hide>${model.name}</td>
<td edit-show><input type="text" value.bind="model.name"></td>
<td><button edit-trigger>Edit</button></td>
</tr>
So basically I want a customAttribute named toggleEdit that will look for edit-trigger attribute and add an event listener to it that will toogle a variable true/false and depending on it will either show or hide the elements that have edit-hide / edit-show.
I'd prefer to not travers the DOM inside the element to find these attributes as it feels jQuerish, is this achievable?
I want to have a customAttribute like this because I have at least 10 elements that will use an edit button and having a variable for each one of them and then use if.bind seems like a bad idea. I could always do inside of the template itself through click.delegate="myShowVar = !myShowVar" but as far as I know puting logic inside html is a bad practice (coming from an angular background).
I would add a plunker/codepen but because of the whole compilation and libraries dependencies this does not seem like an easy task.
Many thanks for any ideas.
Use the contenteditable attribute
I recommend against trying to have a custom attribute automagically handling this for you. You'll probably run into more problems than you'll solve this way. Instead, I recommend that you create an editable property in your view model and bind to it.
The contenteditable attribute is a standard HTML attribute that allows for editing the content of HTML elements, such as DIVs, and is supported out of the box with Aurelia. I recommend leveraging it if it will meet your needs. Here's how:
table.html
<td contenteditable.bind="editable"></td>
<td><button click.delegate="editable = !editable"></td>
Full running gist here: https://gist.run/?id=c4e716f21f4f9c15a9346cfacbdae74b
Since my <tr></tr> turned out to be a 14 line code (with some animation on toggling) I decided it was best to create a customElement out of it. The problem I ran on was that customElements don't really work as table elements (similar as in other frameworks). The soultion to this is to use a as-element attribute.
Inside of the customElement I used contenteditable which is actually a better solution than using if.bind since swapping between input and a div produces a jumping effect because of the difference in styles (which of course could be circumnavigated by applying certain style to them but contenteditable works out-of-the-box).
This is more or less what I created (parent.html):
<tr as-element="my-custom-row"
title="My first row"
is-editable="true"
model.two-way="myModel.name"
value-changed-callback.call="updateModel(myModel)">
</tr>
And inside my-custom-row.html:
<template>
<td>${title & oneTime}</td>
<td>
<div contenteditable.one-way="editingEnabled ? 'true' : 'false'"
blur.trigger="valueChanged(model)"
textcontent.two-way="model">
</div>
<div class="my-class" class.one-way="editingEnabled ? 'my-class--active' : ''"></div>
</td>
<td>
<span class="edit-icon"
click.delegate="editingEnabled = !editingEnabled"
if.one-time="isEditable">
</span>
</td>
</template>
This way I don't have to create a variable for each edit since editingEnabled is unique to each customElement.
As I side note, I think it's better to be more explicit and to use one-way/two-way/one-time instead of bind since it's clear what is happening.
I will also provide the corresponding js for a full answer(my-custom-row.js):
import { bindable } from 'aurelia-framework';
export class MyCustomRow {
#bindable model;
#bindable title;
#bindable isEditable;
#bindable valueChangedCallback;
constructor() {}
valueChanged(modelValue) {
this.valueChangedCallback({ modelValue })
}
}
There are many planbox, which are having same class and ids, inside them there are a number of <p> tags and decorated text.
<div class="planbox">
<p class="baseprice">
<span>
<strike> $70 </strike>
</span>
</p>
<p> New discount price is etc. </p>
</div>
<div class="planbox">
<p class="baseprice">
<span> $70 </span>
</p>
</div>
Now, My test case is - if the base price is strikethrough, only then <p> 'New discount price .. </p> will show, otherwise not.
How to check whether a text is strikethrough or not? And even if we get this how will I check that <p> New discount.. </p> should not show if the text is not striked.
As there is no class in <p> tag on which I can check whether it displayed or not.
One solution in my mind was - add one dummy class in <span> tag and using findChildren('span.dummyCLass') it will result all the webelements having dummyClass.
Now I will check whether web-elements have strike tag or not, and this is the place where I got stuck.
Initially, i was thinking of a Jquery solution, but is it possible to do without adding new class and jquery?
You don't need to add a class to any element to accomplish this task. In general, you don't want to edit the HTML. Another issue is... if you can find the element to add a class, then you don't need to add the class to find the element. :)
The way I approach tasks like these is to find the outermost element that contains all the elements that you are interested in. I refer to this as a "container". What you want to do in this case is to find the containers and loop through them looking for the strikethrough price and for the "New discount price..." text.
The containers are DIVs with the planbox class. The strikethrough price is indicated by the STRIKE tag. The "New discount price..." text is in a P tag. With this info we can write some code. This is Java because I don't know what language you want and I'm not familiar with the Galen framework.
// gets the collection of containers using a CSS selector
List<WebElement> containers = driver.findElements(By.cssSelector("div.planbox"));
// loops through the containers
for (WebElement container : containers)
{
// determine if the STRIKE tag exists
boolean strikeExists = !container.findElements(By.tagName("strike")).isEmpty();
// determine if the "New discount price is..." text exists in the 2nd P tag
boolean discountPriceExists = container.findElements(By.tagName("p")).get(1).getText().trim().contains("New discount price is");
// if both are true log a pass, otherwise log a fail
if (strikeExists && discountPriceExists)
{
// log a pass
}
else
{
// log a fail
}
}
I haven't used much of selenium. But you can port this jquery code to selenium,
//if there is a strike element
if($(".baseprice span strike").length > 0){
//next() will select the sibling of the p tag with baseprice class
$("p.baseprice).next() != undefined){
return true
}else{
return false
}
}
you can use Galen for this. There you can verify certain CSS properties.