angular 2 spinner, slider: custom pipe not working - formatting

The following is the section of my angular component template that is not working:
<p-spinner id="yzDistance"
[min]="aRenderState.clipping.planes[0].min"
[max]="aRenderState.clipping.planes[0].max"
[step]="inputStep"
[(ngModel)]="yzDistance" (onChange)="moveClip(0)">
</p-spinner>
<input type="range" class="slider" type="range" name="yzDistance"
[min]="aRenderState.clipping.planes[0].min"
[max]="aRenderState.clipping.planes[0].max"
[step]="inputStep"
[ngModel]="yzDistance | decimalsPipe"
(ngModelChange)="yzDistance=$event"
(input)="moveClip(0)">
the spinner is working fine showing values formatted correctly e.g 2.009 4.765 -1.649 etc. (3 decimal places). When I move the slider that also has a step of 0.001 the spinner get updated but displays decimals with thousand separators e.g. 3.987,432 -1.34,092 etc. I have tried to correct the problem with the following custom pipe called decimalsPipe:
#Pipe({name: 'decimalsPipe'})
export class DecimalsPipe implements PipeTransform {
transform(value) {
value.toLocaleString('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 3
});
}
}
#Component({
selector: 'myComponent',
templateUrl: './myComponent.html',
styleUrls: ['./myComponent.css']
})
export class myComponent { ...
it still showing the weird decimal formatting and it does not raise errors. Can you help me to sort this out?
Thank you, Dino

u do not return any value in transform , try to return the formatted value in the method transform :
transform(value) {
return value.toLocaleString('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 3
});
}

I have accepted Med_Ali_Rachid answer even if it doesn't work as his answer point me in the right direction. The problem is the spinner that is not formatting the value returned by the pipe.
My solution was to hide the input area of the spinner leaving only the up and down arrows buttons visible. Then I have added a paragraph styled as an input to display the correct value.
<p>
{{yzDistance | decimalsPipe}}
</p>
<p-spinner id="yzDistance"
[min]="aRenderState.clipping.planes[0].min"
[max]="aRenderState.clipping.planes[0].max"
[step]="inputStep"
[(ngModel)]="yzDistance" (onChange)="moveClip(0)">
</p-spinner>

Related

CKEditor 5 Convert Paragraph Model to Div with Inner Span

I'm currently reading the CKEditor 5 docs, and trying to understand how I could achieve the following conversion:
For every paragraph model, convert the view to a div with an inner span.
The following gets me half way there...
editor.conversion.elementToElement({ model: 'paragraph', view: 'div', converterPriority: 'high' })
And now all paragraph models are converted to divs (instead of <p> elements)
But how can I add the additional span element so that each paragraph model is rendered as:
<div><span>Text here...</span></div>
Will I have to switch to more specialized upcast, dataDowncast, and editorDowncast converters? Or can this still be handled via editor.conversion.elementToElement?
Update:
I've tried the following - which is very close:
editor.conversion.for('downcast').elementToElement({
model: 'paragraph',
view: (modelElement, conversionApi) => {
const { writer } = conversionApi
const divElement = writer.createContainerElement('div')
const spanElement = writer.createAttributeElement('span')
conversionApi.mapper.bindElements(modelElement, spanElement)
writer.insert(writer.createPositionAt(divElement, 0), spanElement)
return divElement
},
converterPriority: 'high',
})
However, this outputs the following:
<div>
Text here...
<span></span>
</div>
Struggling to get the modelElement inside the span :-(
For anyone else looking for this, I've posted a working solution here... CKEditor 5 Downcast Converter for Paragraph To Wrap Text in Span

Keep props updated with programatically generated components

Imagine an empty virtual bulletin board where an unknown number of virtual notes will be placed. The board is the parent component and the note is the child.
When I click the board a new note appears on the board. When I move the mouse the note should follow the mouse cursor (weird UI I know, but I'm simplifying for the sake of this post).
I'm generating a new note by instancing it and then adding it to the dom like this:
let NoteClass = Vue.extend(Note);
let note = new NoteClass({
propsData: { x: this.clientX, y: this.clientY },
});
note.$mount();
this.$refs.board.appendChild(note.$el);
Notice the mouse x/y is passed to the note via props. This causes the note to appear at the position of the mouse cursor when I click. Great.
However, once the Note is instanced it no longer updates the x/y props. The Note does not continuously read the position of the mouse cursor from its parent.
Here's the full code:
https://codesandbox.io/s/boring-wiles-pru1y?file=/src/App.vue
For comparison, check out this version where the Note is NOT generated in code. A single note is placed the typical way. It follows the cursor just fine:
https://codesandbox.io/s/proud-tree-xnthc?file=/src/App.vue
I found a way better solution thanks to Michal LevĂ˝'s comment -- the data driven way:
<template>
<div ref="board" class="board" #click.self="onClick" #mousemove.prevent="drag">
<Note v-for="(note, key) in notes" :key="key" :x="clientX" :y="clientY"></Note>
</div>
</template>
<script>
import Note from '#/components/Note.vue';
export default {
name: 'Board',
data() {
return {
clientX: 0,
clientY: 0,
notes: []
}
},
components: {
Note
},
methods: {
onClick() {
this.notes.push({});
},
drag(event) {
this.clientX = event.clientX;
this.clientY = event.clientY;
}
}
}
</script>

Updating a d3 Chart with Vue on chage to data

This is the first time I'm asking a question here, so I hope I can phrase it in a way that makes sense.
I'm just beginning to learn Vue and D3, and I'm making an app that generates a bar chart based on some user data. It is supposed to display a chart representing one user, and then have a list of buttons that you can click to generate the chart that represents each of the other users. Right now, it can generate a chart for each different set of data, but I can't figure out how to make the chart update when a new user is chosen.
The name in the H2 header at the top of the chart updates when bottons are clicked, so I know my "featuredUser" prop is changing, so the buttons with usernames seem to be working (they are in another component):
<template>
<div id="Chart">
<h2>{{ featuredUser.firstName }} {{ featuredUser.lastName }}</h2>
<div class="Chart"></div>
</div>
</template>
<script>
import * as d3 from 'd3';
export default {
props: ["featuredUser"],
name: "Chart",
watch: {
featuredUser() {
this.generateChart();
// the below console log works, even when the chart doesn't update
// so it seems that this varaible is being watched for changes
console.log(this.featuredUser.firstName);
}
},
methods: {
generateChart() {
let qualities = this.featuredUser.qualities;
// EDIT: Adding the following two lines solves the problem
// the remove the previous chart before the new one is generated
d3.select(".Chart")
.selectAll('div').remove();
d3.select(".Chart")
.selectAll('div')
.data(qualities)
.enter().append('div')
.style('width', function (d) { return (d.score * 5)+10 + "em"})
.text(function (d) { return d.quality })
.attr("id", function(d) {return d.type});
},
},
// having the below as 'setup()' allows the chart to be generated on click
// for one user but it doesn't change when another user is clicked,
// having it set as 'mounted()' generates the chart of the chosen user on load,
// but it will not change again.
setup() {
this.generateChart();
}
};
</script>

How can I implement v-model.number on my own in VueJS?

I have a text field component for numeric inputs. Basically I'm just wrapping v-text-field but in preparation for implementing it myself. It looks like this.
<template>
<v-text-field v-model.number = "content" />
</template>
<script>
export default {
name: 'NumericTextField',
props: [ 'value' ],
computed: {
content: {
get () { return this.value },
set (v) { this.$emit('input', f) },
},
}
}
</script>
This has generated user feedback that it's annoying when the text field has the string "10.2" in it and then backspace over the '2', then decimal place is automatically delete. I would like to change this behavior so that "10." remains in the text field. I'd also like to understand this from first principles since I'm relatively new to Vue.
So I tried this as a first past, and it's the most instructive of the things I've tried.
<template>
<v-text-field v-model="content" />
</template>
<script>
export default {
name: 'NumericTextField',
props: [ 'value' ],
computed: {
content: {
get () { return this.value },
set (v) {
console.log(v)
try {
const f = parseFloat(v)
console.log(f)
this.$emit('input', f)
} catch (err) {
console.log(err)
}
},
},
}
}
</script>
I read that v-model.number is based on parseFloat so I figured something like this must be happening. So it does fix the issue where the decimal place is automatically deleted. But... it doesn't even auto delete extra letters. So if I were to type "10.2A" the 'A' remains even though I see a console log with "10.2" printed out. Furthermore, there's an even worse misfeature. When I move to the start of the string and change it to "B10.2" it's immediately replaced with "NaN".
So I'd love to know a bunch of things. Why is the body of the text body immediately reactive when I change to a NaN but not immediately reactive when I type "10.2A"? Relatedly, how did I inadvertently get rid of the auto delete decimal place? I haven't even gotten to that part yet. So I'm misunderstanding data flow in Vue.
Lastly, how can I most simply provide a text box that's going to evaluate to a number for putting into my data model but not have the annoying auto delete of decimal places? The existing functionality doesn't auto delete trailing letters so I'm guessing the auto delete of decimal places was a deliberate feature that my users don't like.
I'm not 100% sure of any of this, but consider how v-model works on components. It basically is doing this:
<v-text-field
v-bind:value="content"
v-on:input="content = $event.target.value"
/>
And consider how the .number modifier works. It runs the input through parseFloat, but if parseFloat doesn't work, it leaves it as is.
So with that understanding, I would expect the following:
When you type in "10.2" and then hit backspace, "10." would be emitted via the input event, parseFloat("10.") would transform it to 10, v-on:input="content = $event.target.value" would assign it to content, and v-bind:value="content" would cause the input to display "10". So then, this is the expected behavior.
When you type in "10.2" and then hit "A", "10.2A" would be emitted via the input event, parseFloat("10.2A") would transform it to 10.2, v-on:input="content = $event.target.value" would assign it to content, and v-bind:value="content" would cause the input to display "10.2". It looks like it's failing at that very last step of causing the input to display "10.2", because the state of content is correctly being set to 10.2. If you use <input type="text" v-model.number="content" /> instead of <v-text-field v-model.number="content" />, once you blur, the text field successfully gets updated to "10.2". So it seems that the reason why <v-text-field> doesn't is due to how Vuetify is handling the v-bind:value="content" part.
When you type in "10.2" and then enter "B", in the beginning, "B10.2" would be emitted via the input event, parseFloat("B10.2") would return NaN, and thus the .number modifier would leave it as is, v-on:input="content = $event.target.value" would assign "B10.2" to content, and v-bind:value="content" would cause the input to display "B10.2". I agree that it doesn't seem right for parseFloat("10.2A") to return 10.2 but parseFloat("B10.2") to return "B10.2".
Lastly, how can I most simply provide a text box that's going to evaluate to a number for putting into my data model but not have the annoying auto delete of decimal places?
Given that the default behavior is weird, I think you're going to have to write your own custom logic for transforming the user's input. Eg. so that "10.2A" and "B10.2" both get transformed to 10.2 (or are left as is), and so that decimals are handled like you want. Something like this (CodePen):
<template>
<div id="app">
<input
v-bind:value="content"
v-on:input="handleInputEvent($event)"
/>
<p>{{ content }}</p>
</div>
</template>
<script>
export default {
data() {
return {
content: 0,
};
},
methods: {
handleInputEvent(e) {
this.content = this.transform(e.target.value);
setTimeout(() => this.$forceUpdate(), 500);
},
transform(val) {
val = this.trimLeadingChars(val);
val = this.trimTrailingChars(val);
// continue your custom logic here
return val;
},
trimLeadingChars(val) {
if (!val) {
return "";
}
for (let i = 0; i < val.length; i++) {
if (!isNaN(val[i])) {
return val.slice(i);
}
}
return val;
},
trimTrailingChars(val) {
if (!val) {
return "";
}
for (let i = val.length - 1; i >= 0; i--) {
if (!isNaN(Number(val[i]))) {
return val.slice(0,i+1);
}
}
return val;
},
},
};
</script>
The $forceUpdate seems to be necessary if you want the input field to actually change. However, it only seems to work on <input>, not <v-text-field>. Which is consistent with what we saw in the second bullet point. You can customize your <input> to make it appear and behave like <v-text-field> though.
I put it inside of a setTimeout so the user sees "I tried to type this but it got deleted" rather than "I'm typing characters but they're not appearing" because the former does a better job of indicating "What you tried to type is invalid".
Alternatively, you may want to do the transform on the blur event rather than as they type.

How to use properties of a Component in vue.js when input text changed?

I'm using Vuesax Framework Components which it's working fine, but my in learning process for both laravel/vue.js and got a problem about how to use properties of component when input text got changed?
Consider this :
<vs-input
style="width:100%"
label="Label"
danger-text="character"
v-model="form.invoice_title"
name="invoice_title"/>
in top code component gonna show me a input text with a red color with text which will tell use what is wrong, but in right idea i should make that red color show when something is wrong with inputs, like `input must be more than 3 Characters'.
Input Component in vuesax
So here's what i wanna do make my input activate/trigger :danger & danger-text properties when something is wrong Like:
inputs must be more than 6 and less than 80 characters.
This can be done via a computed property. eg:
<vs-input
style="width:100%"
label="Label"
:danger="hasError"
:danger-text="errorText"
v-model="form.invoice_title"
name="invoice_title"/>
... // in javascript
computed() {
hasError() {
return this.form.invoice_title.length < 6 || this.form.invoice_title.length > 80
}
errorText() {
if (this.form.invoice_title.length < 6) {
return 'Invoice title should be at least 6 characters'
}
if (this.form.invoice_title.length > 80) {
return 'Invoice title should be at most 80 characters'
}
return ''
}
}
I highly recommend vuelidate lib form managing validation