How to pass data as array of object in stencil js? - stenciljs

I have been trying to pass the data from index.html to table-data component but I'm getting it as undefined. Tried so many ways if I pass data inline in the table data tag its working but if I'm passing explicitly I'm getting undefined.
index.html
<table-data options="data"></table-data>
<script>
var data = [ {name:'a', age: '25'}, {name:'b', age: '25'}, {name:'c', age: '25'}, {name:'d', age: '25'}];
</script>
table-data.tsx
#Component({
tag: 'table-data',
shadow: true
})
export class table-data{
#Prop() options: string;
#State() internalOptions: string[];
componentWillLoad() {
this.parseOptions();
}
#Watch('options')
parseOptions() {
if (this.options) {
this.internalOptions = JSON.parse(this.options);
console.log(this.internalOptions)
}
}
}

Unfortunately, you can't pass "rich" data via html attributes. For that purpose you should use the script tag and assign the element's property like:
<table-data options="data"></table-data>
<script>
const table = document.querySelector('table-data');
table.options = [ {name:'a', age: '25'}, {name:'b', age: '25'}, {name:'c', age: '25'}, {name:'d', age: '25'}];
</script>
You can use string values for attributes as well, but you'll need to JSON.parse them later.

Related

"Is not in camelCase" warning when props are individually generated from a unique object

I pass an object configObject as prop to my child component:
<custom-button v-bind="configObject" />
This object contains various properties which I define individually inside my child component in order to extract the corresponding values:
props: {
button_id: {
type: String,
default: ''
},
is_external_link: {
type: Boolean,
default: true
},
display_button: {
type: Boolean,
default: true
},
...
},
This works perfectly, but Vue complains that these props aren't in camelCase. Yet if I rename them camelCase-style (button_id -> buttonId), the data isn't passed anymore such that buttonId is empty for example.
How to resolve this dilemma?
EDIT: `configObject' looks like below.
{
button_id: '123',
text: 'blabla',
link: 'https://google.com',
is_external_link: true,
image: 'https://cdn.image.jpg',
...
}
I'm not familiar with the syntax you use to pass props, but my approach would be like this:
<custom-button :config="configObject" />
props: {
config: {
type: Object,
default: ()=>({...}) // give schema if you like.
},
},
data() {
return {
...this.config,
otherKey: "other value",
}
}
I ended up accessing, from my child component, the prop object's properties directly - configObject.property, after retrieving it the regular way:
props: {
configObject: {
type: Object,
default: null
}
},

How to declare an array outside function in vue's component syntax

I want to declare a global array inside vue.js component, so that I can access it in all the methods. Where do I declare it in which part of component?
I have tried setting in PROPS but it produces an object while I need an array type.
export default{
name:"BarElement",
props:[
"labels",
"dataset",
"colors"
],
methods:{
drawGraph() {
var dataPoints =[];
var dataPoint =this.getDataPoint(upperLeftCornerX,nextX,value);
this.dataPoints.push(dataPoint);
}
getDataPoint(x, nextX, value) {
return {
'leftEdge':x,
'rightEdge':nextX,
'value':value
}
},
showToolTip(event) {
var mouseX = event.offsetX;
var toolTipVal = this.dataPoints.forEach(function(item, key) {
if(mouseX >= item.leftEdge && mouseX <= item.leftEdge )
return item.value;
});
console.log(toolTipVal);
}
}
Try to declare it inside the data object
data () {
myArray:[]
}
You can also declare props as an object, as this will allow to specify the type of the props
props: {
labels: Array,
dataset: Array,
colors: Object
}
If you want all your components of that type to share the same array, declare it outside of the export default statement.
Well, Inside your component you can declare with data as function and that will be locally to your function.
name: "BarElement",
props: ["labels", "dataset", "colors"],
data: function() {
return {
foo: {
text: 'text1'
}
}
}
In case If you want to declare a global array in your app component.
Similar to the component, you can able to add the data inside it.
Now, to access that data, you can use it with this.$root.[nameOfObject].
Here are the official docs for $root
Hope this helps!

How to use parent's mixin as a child model

Im trying to use a parent's mixin as a child model, but I couldn't make it work as the mixin isn't resolved on the 'data' definition.
Here is a fiddle:
<div id="vue-instance">
<div>
USER: {{user}}
<br/>
EMAIL: {{email}}
</div>
<input-list :field="field" v-for="field in fields"/>
</div>
js:
Vue.component('input-list', {
name: 'InputList',
props: ['field'],
template: '<div>{{field.id}}: <input type="text" v-model="field.model"/></div>'
})
var userData = {
data() {
return {
user: 'foo',
email: 'foo#barbar'
}
}
}
var vm = new Vue({
el: '#vue-instance',
mixins: [userData],
data() {
return {
fields: [
{
id: "UserName",
model: this.user
},
{
id: "Email",
model: this.email
}
]
}
}
});
https://jsfiddle.net/rafalages/rp7bu2qt/9/
The expected result would be update parent mixin value in the child input.
This is not an issue with the mixin, but rather the overall design, if you we to use data instead of mixin, you'd see the same behaviour.
This will not work they way you intend it to.
Two things about Vue worth re-iterating:
You pass props down and emit events up
Mutating a prop will not update the parent
more reading here:
https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
There is a note at the bottom
Note that objects and arrays in JavaScript are passed by reference, so if the prop is an array or object, mutating the object or array itself inside the child component will affect parent state.
But my recommendation is to think of this as a side-effect rather than a feature. You should not rely on the child from updating the value. The only reliable way (outside of other state management) is to pass a function.
Another issue is that referencing this in data during creation will not work
data: {
fields: [
{
id: "UserName",
model: this.user
},
{
id: "Email",
model: this.email
}
]
}
This will not use the data from this.user and will be set to null. You can use the mounted life-cycle function to set these like this:
mounted() {
this.fields = [
{
id: "UserName",
model: this.user
},
{
id: "Email",
model: this.email
},
]
}
which will set it to use the initial values. But referencing an object like this inside another object will also serve to create new bindings, essentially cutting the reactivity. This means that you'll use the values from those objects, but changes to this.fields[1].model will not update this.email
here is a semi-working fiddle that attempts to use your current design
https://jsfiddle.net/rp7bu2qt/113/
notice that...
the binding is using v-model, which is shorthand for binding a prop and an emit/update to a value
you see an error
You are binding v-model directly to a v-for iteration alias. This will not be able to modify the v-for source array because writing to the alias is like modifying a function local variable. Consider using an array of objects and use v-model on an object property instead.
changes to the input fields do not change the original data, just the content of fields[n].model

How can I change the whole data of a vue object?

Simply, I just want to change the entire data of a Vue object, like this:
vueobj.$data = newdata;
but the official document says it's not allowed:
VM156 vue.js:597 [Vue warn]: Avoid replacing instance root $data. Use
nested data properties instead.
(found in )
So I tried another way: first destroy the the vueobj by $destroy(), then create a new vueobj to bind new data object to the same the UI element, but after that, the UI element still uses the old data. So how could I solve this problem? thanks!
this works for me:
<div id="app">
{{name}}: {{age}}
</div>
const app = new Vue({
el: "#app",
data: {
name: "Alice",
age: 30
},
methods: {
refresh(data) {
Object.assign(this.$data, data); // <--
}
}
});
const newData = {
name: "Bob",
age: 22
};
app.refresh(newData);
It is tricky, but you could iterate over the data properties and:
First: remove all data properties;
Second: add each property of the newdata object to the data object.
Demo below:
new Vue({
el: '#app',
data: {
name: 'Alice',
age: 30
},
methods: {
changeWholeData() {
let newdata = {name: 'Bob', age: 40};
// erase all current keys from data
Object.keys(this.$data).forEach(key => this.$data[key] = null);
// set all properties from newdata into data
Object.entries(newdata).forEach(entry => Vue.set(this.$data, entry[0], entry[1]));
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<p>Name: {{ name }}</p>
<p>Age: {{ age }}</p>
<button #click="changeWholeData">Change Whole Data</button>
</div>
As it says, you need to use a nested property to wrap the data you want to replace so it just takes a little planning up front.
If you create your Vue instance like this,
var vueobj = new Vue({
el: '#app',
data: {
dataSource: {
name: 'Alice',
age: 30
}
}
})
then you can replace your data like this
vueobj.dataSource = newdata;
The dataSource wrapper property can be any valid property name. The downside is you can no longer access your inner properties directly on the Vue instance. E.G. you have to use vueobj.dataSource.name instead of vueobj.name, but I guess that's the trade off for all the other ease of use Vue is providing. You could create a name computed to solve this on a case-by-case basis but it would be tedious to do if there are many root properties.

Vue - default values of nested properties

How can I set a default value of a nested property of a Object prop?
Apparently, Vue parse default value of nested properties only if the first level Object prop is undefined.
Example:
Vue.component('example', {
props: {
options: {
type: Object,
default: function() {
return {
nested: {
type: Object,
default: function(){
return 'default value'
}
}
}
}
}
})
Apparently, Vue parse default value of nested properties only if the
fist level Object prop is not undefined.
Yes and it makes sense because if you don't have outer object, you won't be able to have inner or nested properties.
So I think it's even more readable just set as default {} an emtpy object for the first level object and you should make your own defensive validations against undefined or null, like the bellow example:
<script>
export default {
props: {
option: {
type: Object,
default: () => {},
required: false
}
},
computed: {
optionReceived: function () {
const defaultNestedValue = 'Some default value'
const option = this.option.nested || defaultNestedValue;
return option;
}
}
}
</script>
I think it is always better to make your data structure easy to use and as flat as possible. Because nested props in Vue is never a good choice.
Assume the options you mentioned in your Vue component have a lot of properties inside.
Example:
props: {
options: {
bookAttributes: {
colorAttributes: { coverColor: 'red', ribbonColor: 'green' },
sizeAttributes: { coverSize: 10, ribbonSize: 2 },
...
}
}
}
you could make them flat like this for better comprehension.
props: {
coverSize: 10,
coverColor: 'red',
ribbonColor: 'green,
ribbonSize: 2 ...
}
And then you and your colleagues could happily use your component like this:
<your-component>
coverSize="15"
coverColor="blue"
ribbonColor="red"
ribbonSize="3"
</your-component>
Good luck and wish you well.