Polymer property not updating when set from Javascript - data-binding

I have made this simple property (Polymer 2.x):
static get properties() {
return {
bpm: {
type: Number,
value: () => {
return 0
},
observer: "_bpm"
}
}
}
I tried to update it using this.bpm = 60; in a function called when clicking a button. If I output the value using console.log(this.bpm); it displays the correct value, but my heading <h2 id="bpm">[[bpm]]</h2> is not updated and the observer is not called.
When bpm is set using something like <paper-slider value="{{bpm}}"></paper-slider> it works.
What am I doing wrong? Thank you for your help!

It will be easier for the community to know that this question was answered into the comments of the requests.
Initial problem : Binding value not updated because bpm property was set from a function outside of the element.
Correction : Here a working JSFiddle (to use in chrome) used to demonstrate how to use the binding.

I also faced similar issue due to setting the property from a different function. Putting it here for reference.
My code:
Polymer({
is: 'test-test',
properties: {
min: {
type: Number,
value: -1,
observer: '_minChangedd'
}
},
_minChangedd: function (val) {
console.log(val);
},
ready: function () {
setInterval(function () {
this.min = this.min + 1;
}, 500);
},
});
Problem:
The setInterval function had its own this and so the expression this.min actually refers to min of setInterval.
Using arrow functions resolved the issue, by replacing the call with setInterval(() => {...});

Related

Computed Value not undefined

i want to filter out the prop in computed, the prop value is available, but computed always shows undefined. Following is my code :
export default {
name: "Validation",
props: {
validationResult: {
type: Object,
required: true,
},
},
computed: {
filteredInvalidRules() {
return this.validationResult.sss.rules.filter((rule) => rule.isValid === false);
},
},
'Validation Results' is aavailable , and has value in dev Tools.
The computed property 'filteredInvalidRules' is always undefined. How to fix it?
TIA
I guess it's undefined because of the component creating steps.
I think it goes something like this (please correct me if I'm wrong):
the child component is created.
your computed property looks after validationResult.
validationResult is required, but has no value yet.
filteredInvalidRules returns undefined
your parent component is created and you pass the data (and you see them in your devTools).
Solution 1: add default values to your props
props: {
validationResult: {
type: Object,
default() {
return {
sss: {
rules: {
isValid: false
}
}
};
},
required: true,
},
},
so when your child component is created, your computed property got access to the default property values.
Solution 2: return in computed property
filteredInvalidRules() {
const isUndefined = obj2?.sss?.rules === undefined
if (isUndefined) return []
return this.validationResult.sss.rules.filter((rule) => rule.isValid === false);
},
this approach will return an empty array on creating the child component. I think it's better to use default values, to keep the computed property clean and to prevent the isUndefinedcheck each time a value changes.
There could be more solutions to it.

Detect change to modelValue in Vue 3

Is there a way to detect change to modelValue in a custom component? I want to push the change to a wysiwyg editor.
I tried watching modelValue but emitting update for modelValue triggered that watch, which created circular data flow.
Code:
export default {
props: ['modelValue'],
watch: {
modelValue (val) {
this.editor.editor.loadHTML(val)
}
},
mounted () {
this.editor.editor.loadHTML(val)
this.editor.addEventListener('trix-change',
(event) => this.$emit('update:modelValue', event.target.value))
}
}
<TextEditor v-model="someHtml"></TextEditor>
In VueJS v3, the event name for custom v-model handling changed to 'update:modelValue'.
You can listen to these events like this: v-on:update:modelValue="handler"
For a more complete example, lets assume you have a Toggle component with these properties/methods:
...
props: {
modelValue: Boolean,
},
data() {
return {
toggleState: false,
};
},
methods: {
toggle() {
this.toggleState = !this.toggleState;
this.$emit('update:modelValue', this.toggleState);
}
}
...
You can use that Toggle component:
<Toggle v-model="someProperty" v-on:update:modelValue="myMethodForTheEvent"/>
As a side note, you could also v-model on a computed property with a setter; allowing you to internalise your state changes without using the update:modelValue event. In this example, it assumes you v-model="customProperty" on your custom Toggle component.
computed: {
customProperty: {
get() {
return this.internalProperty;
},
set(v) {
this.internalProperty = v;
console.log("This runs when the custom component 'updates' the v-model value.");
},
}
},
I had the same problem and solved it using a slight tweak to the way you call the watch function:
setup(props) {
watch(() => props.modelValue, (newValue) => {
// do something
})
}
Hence, the important thing is to add () => props.modelValue instead of just putting props.modelValue as the first argument of the watch function.
try that:
watch: {
...
modelValue: function(val) {
console.log('!!! model value changed ', val);
},
...

Adding styles to dynamically created component

I’m working on content driven Angular apps.
To give an overview,
API will share the rich text JSON.
Based on type field from response, components will be created dynamically.
Below is the sample JSON,
{
text: 'This is paragraph',
}
Below code works for only one item, if I get multiple text items, it doesn't work.
text(offset, length, text: string) {
return text.substr(offset, length);
}
stlye(type: string, text: string): string {
switch (type) {
case 'blold': return `<b>${text}</b>`;
}
}
The problem you are having is with timing. I was unable to find out exactly where, but at the time the bold change was made, you eventually overwritten the original modified content. If you put a console log in the snippet that stylizes your component will understand what I'm saying
setTimeout(() => {
this.styleList.forEach(styleItem => {
console.log(this.test.nativeElement.innerHTML)
this.renderer.setProperty(this.test.nativeElement, 'innerHTML', this.test.nativeElement.innerHTML.replace(styleItem.styleText, styleItem.type));
});
}, 1000);
I have not figured out exactly where the sync problem is, but I've managed to produce the expected result that might help you better understand the problem.
setTimeout(() => {
this.styleList.forEach(styleItem => {
const elements = this.test.nativeElement.getElementsByTagName('bw-information-plain-text')
for (var i=0; i<elements.length; i++) {
this.renderer.setProperty(elements[i], 'innerHTML',
elements[i].innerHTML.replace(styleItem.styleText, styleItem.type));
}
});
}, 1000);
good luck

"TypeError: Cannot set property 'VibrantImg' in RactiveJS adaptor

I'm trying to create a simple Ractive adaptor to parse a value from the Color Thief (http://lokeshdhakar.com/projects/color-thief/) into a template with a defined mustache. (I know there may be better ways to achieve this, but there is a reason for why I'm using the adaptor route!)
I've set up a demo of what I have so far here - this the Ractive code part:
var colorThief = new ColorThief();
var img2 = document.getElementById('ctimage');
var imgColor;
Ractive.adapt.CTImg = {
filter: function ( object ) {
return object instanceof img2;
},
wrap: function ( ractive, img2, keypath, prefixer ) {
// Setup
return {
teardown: function(){
colorThief.destroy();
},
get: function(){
imgColor = colorThief.getColor(img);
},
set: function(property, value){
ractive.set('mainColor', imgColor);
},
reset: function(value){
}
}
}
};
var ractive = new Ractive({
target: '#container',
template: '#template',
adapt: [ 'CTImg' ],
data: {
mainColor: "rgb(97, 79, 112)" // this is what should be returned
}
});
My aim is to get the prominent color from the image given in the Codepen (above), pass it into Ractive (and to Color Thief by the adaptor), then output the resulting color on screen in the relevant mustache.
I can display a hard coded color OK in the template, so I know that the data keypath / reference is OK. However, my issue is getting the color back from Color Thief via the adaptor - the error I'm getting is Uncaught "TypeError: Cannot set property 'CTImg' of undefined".
I've checked through SO and the Ractive Github site to see if I can figure out what is going wrong, but my head is starting to spin!
Can anyone please help me to at least get the color to come back from Color Thief via the adaptor?
So adapt and adaptors are two different config objects. adaptors is a registry of adaptor definitions and adapt tells the component/instance what adaptors to use. There's no global adapt property.
For global registration of an adaptor, you need Ractive.adaptors.
Ractive.adaptors.CTImg = {...}
The next problem is actually how you use the adaptor. Adaptors require you to put the non-POJO data into the instance. The filter is run on the data and determines if the data needs to be adapted, and if so, does the setup. Then, it's the usual adaptor setup. get returns the value to Ractive, set sets the value to your custom object, etc.
Here's an updated example:
Ractive.adaptors.CTImg = {
filter: function ( object ) {
// Detect if the data is an image element
return object instanceof HTMLImageElement;
},
wrap: function ( ractive, object, keypath, prefixer ) {
// Set up color thief for this piece of data because it's an image
var colorThief = new ColorThief();
return {
teardown: function(){
colorThief.destroy();
},
get: function(){
// Return the replacement data
return colorThief.getColor(object);
},
set: function(property, value){
// We're not setting to color thief, leave empty
},
reset: function(value){
// Always replace the data when the data is changed
return false;
}
}
}
};
var ractive = new Ractive({
target: '#container',
template: '#template',
adapt: [ 'CTImg' ],
data: {
dominant: null
},
onrender: function(){
// set image on data. adaptor will capture it.
this.set('dominant', this.find('#ctimage'))
}
});

React rendering recursion stops without error

I've encountered a problem with rendering some elements in React.
(I use ImmutableJS)
renderComponents: function(components) {
if(components.isEmpty()) return [];
var table = [];
components.map(function(component) {
table.push(<ComponentTableElement key={ component.get('id') } data={ component } />);
if(component.has('children')) {
var children = component.get('children');
table.concat(this.renderComponents(children));
}
});
return table;
},
As I looked for error, I found that this.renderComponents(children) doesn't return anything at all and the code somehow stops.
I mean before that line everything works ok, but then after this line, when i try to console.log something, it doesn't show up. And it doesn't even reach return table.
So what is wrong with that code?
In the context of the function you pass to map, this refers to the window object, not to the current component instance, so this.renderComponents is undefined when you try to call it.
components.map(function(component) {
this === window;
});
You can pass a value to use as this in the body of your function as the second parameter of Array::map.
components.map(function(component) {
table.push(<ComponentTableElement key={ component.get('id') } data={ component } />);
if(component.has('children')) {
var children = component.get('children');
// here, `this` refers to the component instance
table.concat(this.renderComponents(children));
}
}, this);
If you're using ES6, you can also use fat-arrow functions, which are automatically bound to this.
components.map((component) => {
table.push(<ComponentTableElement key={ component.get('id') } data={ component } />);
if(component.has('children')) {
var children = component.get('children');
// here, `this` refers to the component instance
table.concat(this.renderComponents(children));
}
});

Resources