stencil is not rerendeing component when #state() decorated property is changed - stenciljs

I started with stencil today. Following code is all I have in my project. Docs say, if Component member decorated with #state() is changed, component will rerender.
Any changes to a #State() property will cause the components render function to be called again.
But even this simple thing is not working. Pls advice.
import {Component, State} from '#stencil/core';
#Component({
tag: 'my-component',
styleUrl: 'my-component.css',
shadow: true
})
export class MyComponent {
#State() name:string = 'john';
changeName(){
this.name = "Peter";
console.log('inside changeName');
}
render() {
return <div>
<p>{this.name}</p>
<button onClick={this.changeName}>click</button>
</div>;
}
}
When I click on the button inside changeName is logged but no changes to name is seen in view.

Try changing your onClick to an arrow function:
<button onClick={() => this.changeName()}>click</button>
This preserves the meaning of this to reference your class. See https://stenciljs.com/docs/templating-jsx/#handling-user-input for more details and examples.

#matthewsteele answer is right but you can also define your function like below to make it work.
private changeName = () => {
this.name = "Peter";
console.log('inside changeName');
}
Doing above the this reference will still preserves to the class.

Related

How to listen events in react/angular/vue from stencils components

I have created simple one component in stencils.js and I used that in react/angular/vue. From my stencil component i have emit an event. This event is captured in angular/react/vue.
My problem is I need to use the same component twice in a single html. How I can identify the which event is emitted by which component instance ?
Using #stencil/react-output-target library and following this guide you will be able to do this:
//button component
import { Component, Prop, h } from '#stencil/core';
#Component({
tag: 'button-component',
styleUrl: 'button.css',
shadow: false,
scoped: true
})
export class MyComponent {
#Prop() label: string;
render() {
return <button type="button">{this.label}</button>;
}
}
// button component instance in React
<ButtonComponent label='Test Button' onClick={() => alert("Hi, I'm a button alert!")}/>
Just found it after hours of trying to pass a function inside of Stencil component...

Can't acces prop inside method

I'm creating a single file component in Vue which takes an object as a prop called data. I want to access this prop inside a custom function that I'm creating. Here's my code
<script>
import { db } from "../main";
export default {
name: "UserDetail",
props: ["data"],
methods: {
verifyUserProfile: () => {
console.log(this.data);
db.collection("users")
.doc(this.data.uid)
.update({
idProofVerified: true
})
}
}
};
</script>
When I use the data prop in template it works just fine but in the function its giving me Cannot read property 'data' of undefined error. What am I doing wrong?
Update: I'm using this component inside View like so:
<div>
<UserDetail :data="users[selected]" />
</div>
Where users is an array of users objects.
You're using () => {} so this points to the global context. Replace it with verifyUserProfile: function () { ... }.
You are most likely calling the method before the props have arrived at the component, typically the props come in only after the lifecycle methods and therefore if you call a function which tries to access them there it will be undefined.
A solution to this would be to place a watcher on the props and then trigger the function once the props have received a value.
Like so:
watch: {
data: {
immediate: true,
handler(val){
if (val){
this.verifyUserProfile()
}
}
}
}

Call a child component method from parrent using vue / composition api

I'm trying to build a reusable modal component in Vue using this composition API. The plan is to expose a few methods like toggleModal() to call on some event in a parent component.
I've written my method in both setup() and methods.
export default {
setup() {
const isModalOpen = ref(false);
const toggleModal = () => {};
return {
toggleModal,
};
},
methods: {
toggleModalMethod() {},
},
};
If I console.log() my modal component I can see that only my toggleModalMethod() from methods is exposed.
Is there a way to expose a child method and call it from a parent component?
It's expected that a property returned from setup will be available on component instance when a child is mounted.
Here is a demo:
<template>
<div>
<Modal ref="modal"/>
<button #click="toggle">Toggle</button>
</div>
</template>
<script>
...
export default {
...
methods: {
toggle() {
this.$refs.modal.toggleModal();
}
}
};
</script>
Accessing child's members from a parent via a ref is considered an edge case, although exposing modal toggle function as public method is widely accepted scenario.

The object sent as prop is undefined while testing a Vue SFC with Jest

I want to test a Vue single file component which receives a prop as input. When I mock the prop, which is an object, I get an error that the object is undefined, The error comes from the HTML where the values of the object are used. If I make the prop to be a string for example (and I remove answer.value and :class="{'active': answer.selected}" from HTML), everything works fine.
Component:
<template>
<div class="answer-container" #click="setActiveAnswer()" :class="{'active': answer.selected}">
<div class="answer">
<p>{{answer.value}}</p>
</div>
</div>
</template>
<script>
export default {
name: 'Answer',
props: {
answer: Object,
},
methods: {
setActiveAnswer() {
this.$emit('selectedAnswer', this.answer);
}
}
}
</script>
Test file:
import { mount } from '#vue/test-utils'
import Answer from './../../src/components/Answer'
describe('Answer', () => {
it('should receive "answer" as prop', () => {
const answer = {
value: 'testAnswer',
selected: true
};
const wrapper = mount(Answer, {
propsData: {
answer: answer
}
});
expect(wrapper.props().answer.value).toBe('testAnswer');
})
})
The error I get is:
TypeError: Cannot read property 'selected' of undefined
Please advise what am I doing wrong. Thanks!
I managed to fix this by adding a v-if="answer" on <div class="answer-container" ..., which is quite strange (as this is not async data) since the code works fine when checking the application in the browser - the problem only appeared while unit testing the component. I suppose there is also a fix in a Jest/Unit testing way, something like declaring the prop after the component finished rendering/mounting...

Getting React unique ID in didMount

I have a custom react Input component. I want all my inputs to have a small hint beside them (an asterix) that on hover shows a hint. The problem is I cannot make the initialization of the popup point to this exact asterix so it shows the message for this particular component. Right now it just replaces the message with the message of the last mounted component.
My question is - how would I reference the exact element. Can I get the React ID from didMount? Can I point to it using render, something like $(this.render() + ' i') -> (ideal case).
import React, { Component, PropTypes } from 'react'
export default class Input extends Component {
componentDidMount() {
var html = this.props.popup;
console.log(this);
$('.inverted.asterisk.icon').popup({
html: html,
variation: 'inverted'
});
}
render() {
return (
<div className="ui icon fluid input">
<input
type={this.props.type}
value={this.props.value}
onChange={this.props.onChange}
name={this.props.name}
/>
<i className="inverted disabled asterisk link icon" />
</div>
)
}
}
Input.propTypes = {
type: PropTypes.string,
name: PropTypes.string,
popup: PropTypes.string,
value: PropTypes.node,
onChange: PropTypes.func
}
You can assign a ref attribute to any element inside your render function to get a reference to it.
Assign the ref
<i ref="icon" className="inverted disabled asterisk link icon" />
Use it in componentDidMount
componentDidMount() {
var html = this.props.popup;
console.log(this);
$(this.refs.icon).popup({
html: html,
variation: 'inverted'
});
}
jsfiddle example