How does an aframe entity defaults to the schema ones if no data is passed on - schema

I'm having some difficulties in understanding how in aFrame an entity works with schema and data together. Forgive me, I'm a bit new in the library and maybe I'm missing something. Thanks.
Consider the following:
// register the aframe component (Button.js):
import { registerComponent, THREE } from 'aframe'
const button = registerComponent('button', {
schema: {
width: { type: 'number', default: 0.6 },
height: { type: 'number', default: 0.40 },
color: { type: 'string', default: 'orange' },
},
init: function() {
const schema = this.schema // Schema property values.
const data = this.data // Component property values.
const el = this.el // Reference to the component's entity.
// Create geometry.
this.geometry = new THREE.BoxBufferGeometry(data.width || schema.width.default, data.height || schema.height.default, data.depth || schema.depth.default)
// Create material.
this.material = new THREE.MeshStandardMaterial({ color: data.color || schema.color.default })
// Create mesh.
this.mesh = new THREE.Mesh(this.geometry, this.material)
// Set mesh on entity.
el.setObject3D('mesh', this.mesh)
}
})
export default button
// add the entity in the app (App.js)
import button from './Button'
<a-entity button="width: 0.4; height: 0.4: color: 'green'" position="0 0 0"></a-entity>
Now with the example above I would expect that my component will use data instead of schema defaults. And if I would instead just do:
<a-entity button position="0 0 0"></a-entity>
It would use the schema default ones.
Is this how it should be done?
I've been seeing a lot of examples where people just use data.
Thanks

The schema isn't intended to be used directly. From the docs:
The schema defines the properties of its component. (...)
The component’s property type values are available through this.data
The idea goes like this:
You define the properties (with defaults) in the schema
schema {
prop: {default: "default value"}
},
You access the values with this.data[prop_name]:
init: function() {
console.log("prop value at init:", this.data.prop)
},
You can monitor the property updates (like via setAttribute("propname", "newValue") within the update handler:
update: function(oldData) {
console.log("prop changed from", oldData.prop, "to", this.data.prop)
},
In a setup like this:
<!-- this.data.prop will contain the default values -->
<a-entity foo></a-entity>
<!-- this.data.prop will contain the new value -->
<a-entity foo="prop: value></a-entity>

Related

(Vue 3) Error: AG Grid: cannot get grid to draw rows when it is in the middle of drawing rows

-- Initial setup --
Create component
const ButtonAgGrid= {
template: "<button>{{ displayValue }}</button>",
setup(props) {
const displayValue = 'TEST-TEXT';
return {
displayValue,
};
},
};
Register component
<AgGridVue
:components="{
ButtonAgGrid
}"
...
/>
Pass data
const columnDefs = [
{
field: "name"
},
{
field: "button",
cellRenderer: "ButtonAgGrid",
}
]
const rowData = computed(() => {
return {
name: testReactiveValue.value ? 'test', 'test2'
}
})
And when computed "rowData" updated, agGrid send error:
Error: AG Grid: cannot get grid to draw rows when it is in the middle of drawing rows. Your code probably called a grid API method while the grid was in the render stage. To overcome this, put the API call into a timeout, e.g. instead of api.redrawRows(), call setTimeout(function() { api.redrawRows(); }, 0). To see what part of your code that caused the refresh check this stacktrace.
But if we remove cellRenderer: "ButtonAgGrid", all work good
My solution is to manually update rowData.
watchEffect(() => {
gridApi.value?.setRowData(props.rowData);
});
This one works well, but I wish it was originally

Get id of item clicked and use it for creating dynamic url in vuejs

I have a vue bootstrap table displaying, in each row, few properties of objects of an array (got through an api call with axios).
Every row has a button that should redirect me to a detail page, with more properties of that object, plus a map.
I was thinking to make a function to get the property id of the object contained in the clicked row, but I'm not sure on how to do it. I need the id to use it in the last part of the api call.
The store is structured so that I have a module for the user and another one for these objects (activities). In these modules I deal with state, actions and mutations. A separate file handles the getters. As these activities will be modified, I need to save their state too.
I will also need to be able to easily access all the properties of the single object (not only the ones shown in the table row) from other components.
I'm getting very confused.
Here the code:
Table with all the activities:
<b-table
responsive
:fields="fields"
:items="activity"
>
<template
slot="actions"
>
<b-button
v-b-tooltip.hover
title="Mostra dettagli"
variant="info"
class="px-3"
#click="goToActivityDetail"
>
<span class="svg-container">
<svg-icon icon-class="search"/>
</span>
</b-button>
</template>
</b-table>
In the script:
export default {
name: 'AllActivities',
data() {
return {
fields: [
{ key: 'activity.activityName', label: 'Activity', _showDetails: true},
{ key: 'related_activity', label: 'Related activity', _showDetails: true},
{ key: 'start', label: 'Start', _showDetails: true },
{ key: 'end', label: 'End', _showDetails: true },
{ key: 'travel_mode', label: 'Travel mode', _showDetails: true },
{ key: 'actions', label: '' }
],
activity: [],
methods: {
getIdActivity(){
**?? how to get it ??**
},
goToActivityDetail() {
this.$router.push({
name: 'activityDetail'
})
}
}
goToActivityDetail()
obviously does not work, in the console:
- [vue-router] missing param for named route "activityDetail": Expected "activityId" to be defined
- [vue-router] missing param for redirect route with path "/see-all-activities/:activityId": Expected "activityId" to be defined)
In the getters file I have:
const getters = {
sidebar: state => state.app.sidebar,
device: state => state.app.device,
token: state => state.user.token
}
export default getters
So here I will need to have something like:
activityId: state => state.activity.activityId
Which is coming from activity.js, which is:
import {
getActivityId
} from '#/components/AllActivities'
const state = {
activityId: getActivityId()
}
const mutations = {
SET_ACTIVITY_ID: (state, activityId) => {
state.activityId = activityId
}
}
const actions = {
setActivityId({
commit
}) {
return new Promise(resolve => {
commit('SET_ACTIVITY_ID', '')
resolve()
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
IF this is right, what is left is the function to get the id of the object contained in the table row clicked.
Also, how to write that activity id in the api call (axios)?
Now I have:
export function getSingleActivity() {
return request({
url: 'http://localhost:8000/api/user_activity/:activityId',
method: 'get'
})
}
But I am not sure if that's correct.
Also, how to access the other properties (to be displayed in the detailActivity page)?
This will be made of a list of some properties (probably a stacked table component) and a map component, so I will need to access the properties in both these components.
I hope I've been clear enough,
thank you.
It was dead simple. I post how to solve it in case someone else get stuck on this too.
I added a slot scope to the template that contains the button:
<template
slot="actions"
slot-scope="data"
>
Then I added the single activity (following the vue bootstrap markup data.item) as parameter to the button click
#click="goToDetailActivity(data.item)"
And the function called by the click became:
goToDetailActivity(activity) {
this.$router.push({
name: 'DettaglioAttivita',
params: { activityId: activity.id }
})
}
That's it.
Worth mentioning is you're using vuex. If I understand correctly you want to get the property read from vuex?
To read a property from vuex you can eather use this.$store.getters.activity
Or use mapGetter.
Read the following page https://vuex.vuejs.org/guide/getters.html
Also you have to set the param when you do a router.push
router.push({ name: 'activity', params: { id: activityId } })

Why it is hard to use vue-i18n in vue data() (why it is not reactive)

I am using vue-i18n in a vue project. And I found it really confusing when using some data in vue data with i18n. Then if I change locale, that data is not reactive. I tried to return that data from another computed data but anyways it is not reactive because i18n is written in data. *My situation - * I want to show table with dropdown(list of columns with checkbox) above it. When user checks a column it will be showed in table if unchecks it won't. It is working fine until I change locale. After changing locale table columns is not translated but dropdown items is reactively translated and my code won't work anymore. Here is some code to explain better: In my myTable.vue component I use bootstrap-vue table -
template in myTable.vue
<vs-dropdown vs-custom-content vs-trigger-click>
<b-link href.prevent class="card-header-action btn-setting" style="font-size: 1.4em">
<i class="fa fa-th"></i>
</b-link>
<vs-dropdown-menu class="columns-dropdown">
<visible-columns :default-fields="columns" #result="columnListener"></visible-columns>
</vs-dropdown-menu>
</vs-dropdown>
<b-table class="generalTableClass table-responsive" :fields="computedFieldsForTable">custom content goes here</b-table>
script in myTable.vue
data(){
return {
fieldsForTable: [];
}
},
computed: {
computedFieldsForTable () {
return this.fieldsForTable;
},
columns() {
return [
{
key: 'id',
label: this.$t('id'),,
visible: true,
changeable: true
},
{
key: 'fullName',
label: this.$t('full-name'),,
visible: true,
changeable: true
},
{
key: 'email',
label: this.$t('email'),,
visible: true,
changeable: true
}
]
}
},
mounted () {
this.fieldsForTable = this.filterColumns(this.columns);
},
methods: {
filterColumns(columns = []) {
return columns.filter(column => {
if (column.visible) {
return column
}
})
},
columnListener ($event) {
this.fieldsForTable = this.filterColumns($event)
}
}
Can someone give me some advice for this situation ?
*EDIT AFTER SOME DEBUGGING: I think when filtering columns(in computed) and returning it for fieldsForTable inside filterColumns(columns) method, it actually returning array(of objects) with label='Label Name' not label=this.$t('labelName'). So after filtering the new array has nothing to do with vue-i18n. My last chance is reloading the page when locale changes.
Trying modify computedFieldsForTable as follows. You need to reference this.columns in computedFieldsForTable, so that Vue can detect the change of labels in this.columns.
computedFieldsForTable () {
return this.filterColumns(this.columns);
},
EDITED: put your this.columns in data. Then
columnListener ($event) {
this.columns = $event;
}
I hope i didn't misunderstand what you mean.
EDITED (again):
Maybe this is the last chance that I think it can work. Put columns in computed() still and remove computedFieldsForTable. Finally, just leave fieldsForTable and bind it on fields of <b-table>.
watch: {
columns(val) {
this.fieldsForTable = this.filterColumns(val)
}
},
method: {
columnListener ($event) {
this.fieldsForTable = this.filterColumns($event)
}
}
However, I think it is better and easier to reload page whenever local change. Especially when your columns have a more complex data structure.

Selecting edges based on both edge's data and target's data

I would like a selector for edges matching
edge[type="blocker"]
that have a target matching
node[status="complete"]
In other words, is there a valid way of expressing the following:
cytoscape({
...
style: [
...
{
selector: '( node -> node[status="complete"] )[type="blocker"]',
style: {
display: 'none',
},
},
...
],
...
})
I don't see a way of doing this in the documentation.
Obviously, I could copy the target's data into the node's data and use the following:
edge[type="blocker"][target_status="complete"]
But duplicating the data goes against my every instinct as a software developer.
You can provide function to filter method:
cy.edges().filter(function(ele) {
return ele.data('type') == 'blocker' &&
ele.target().data('status') == 'complete';
})
How about this
EDIT:
var selectedEdges = cy.nodes("[status= 'complete']").connectedEdges("[type = 'blocker']);
var selectedEdges.addClass('specialSnowflake')
And in your stylesheet, just define:
{
"selector": "edge.specialSnowflake",
"style": {
"display": "none"
}
}
If a selector does not suffice, then you could (1) suggest a new feature to enhance the edge -> selector and perhaps even make a PR for it or (2) use a function on the style property instead.
E.g. for (2):
{ selector: 'edge', style: { display: edgeIsDisplayed } }
where the function can be anything, like edgeIsDisplayed(edge) => edge.data('foo') === 'bar' && edge.target().hasClass('baz')
See http://js.cytoscape.org/#style/mappers
There's no selector that will help.
However, it is possible to avoid having to manually update two elements when data changes.
Style sheet value functions are called every time a matching element's data changes. In one those function, one could therefore update the data of incoming edges every time the node's data is updated, keeping the data of the two in sync automatically.
var push_status = function(node) {
node.incomers('edge').forEach( edge => edge.data('target_status', node.data('status')) );
node.outgoers('edge').forEach( edge => edge.data('source_status', node.data('status')) );
};
cytoscape({
...
style: [
...
{
selector: 'node',
style: {
label: node => { push_status(node); return node.data('id'); },
},
},
{
selector: 'edge[type="blocker"][target_status="complete"]',
style: {
display: 'none',
},
},
...
],
...
})
This would qualify as a hack, but it works perfectly. Updating the node's data updates the edge's data, which causes the style to be applied or unapplied as appropriate.
Be wary of creating an infinite loop! In particular, modifying the data of a node's parent will trigger a calculation of the node's style. This problem can be avoided by replacing
ele.data('key', val)
with
// Making changes to a element's data triggers a style recalculation.
// This avoids needlessly triggering the style recalculation.
var set_data = function(node, key, new_val) {
let old_val = node.data(key);
if (new_val != old_val)
node.data(key, new_val);
};
set_data(ele, 'key', val)

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.