How to access vm.$el properties with single-file components? - node.js

I'm trying to access the clientHeightproperty of a vue instance created by a single-file component, but it returns undefined. How can I do this?
<template lang='jade'>
article#article.projectCard.relative.mb5.pa5( v-bind:style="styleObject")
h3 {{ project.projectName }}
p {{ project.projectDescription }}
</template>
<script>
export default {
props: {
project: '',
},
data () {
return {
styleObject: {
backgroundColor: this.project.projectMainColor,
height: '80vh'
},
cardHeight: this.clientHeight,
};
},
</script>

You can access the element after it's mounted with this.$el so you'd actually want this.$el.clientHeight after it's mounted.
You can do like:
data () {
return {
cardHeight: 0,
}
}
Then do:
mounted () {
this.cardHeight = this.$el.clientHeight + 'px'
}
Also, that styleObject would be better as a computed property. That way as things change it'll be automatically updated.
I'd personally do:
data () {
return {
cardHeight: '80vh',
}
},
mounted () {
this.cardHeight = this.$el.clientHeight + 'px'
},
computed: {
styleObject () {
return {
backgroundColor: this.project.projectMainColor,
height: this.cardHeight,
}
}
}

Related

Use XState in a nuxt composable

I want to use an xstate state machine in Nuxt 3 which is used over multiple components.
I created a small example of how I want this to look like.
I also use the nuxt-xstate module.
State Machine:
export default createMachine(
{
id: 'toggle',
initial: 'switched_off',
states: {
switched_on: {
on: {
SWITCH: {
target: 'switched_off'
}
}
},
switched_off: {
on: {
SWITCH: {
target: 'switched_on'
},
}
},
},
}
)
Composable:
const toggle = useMachine(toggleMachine)
export function useToggleMachine(){
return { toggle }
}
app.vue:
<template>
<div>
State: {{toggle.state.value.value}}
</div>
<br />
<button
#click="toggle.send('SWITCH')"
>
Switch
</button>
</template>
<script>
import { useToggleMachine } from '~/composables/toggle_machine'
export default {
setup(){
const { toggle } = useToggleMachine()
return { toggle }
}
}
</script>
The problem is, that I can have a look at the state of the machine {{state.value.value}} gives me the expected 'turned_off'. But I cannot call the events to transition between states. When clicking on the button, nothing happens.
Here is the console.log for the passed 'toggle' object:
Does anyone know a way how to fix this, or how to use xstate state machines over multiple components.
I am aware that props work, but I don't really want to have an hierarchical approach like that.
In Nuxt3, it's very simple:
in composables/states.ts
import { createMachine, assign } from 'xstate';
import { useMachine } from '#xstate/vue';
const toggleMachine = createMachine({
predictableActionArguments: true,
id: 'toggle',
initial: 'inactive',
context: {
count: 0,
},
states: {
inactive: {
on: { TOGGLE: 'active' },
},
active: {
// eslint-disable-next-line #typescript-eslint/no-explicit-any
entry: assign({ count: (ctx: any) => ctx.count + 1 }),
on: { TOGGLE: 'inactive' },
},
},
});
export const useToggleMachine = () => useMachine(toggleMachine);
In pages/counter.vue
<script setup>
const { state, send } = useToggleMachine()
</script>
<template>
<div>
<button #click="send('TOGGLE')">
Click me ({{ state.matches("active") ? "✅" : "❌" }})
</button>
<code>
Toggled
<strong>{{ state.context.count }}</strong> times
</code>
</div>
</template>

Using CKEditor in a component with Laravel/Inertia

I'd like to use Ckeditor in my Laravel/Inertia project and i can't get it to work. I found a tutorial from LaraTips, but that was written for VueJS-2. I am working with the lastest version Inertia which uses VueJS-3.
I want to use Ckeditor in a separate component, and it (sort of) works, but i can't get the old data to show in the editor. I get an error "Uncaught (in promise) TypeError: Cannot read property 'setData' of undefined at Proxy.modelValue (app.js:29)"
What am i doing wrong?
This is my component:
<template>
<ckeditor :editor="editor" v-model="text" :config="editorConfig"></ckeditor>
</template>
<script>
import ClassicEditor from '#ckeditor/ckeditor5-build-classic';
export default {
data() {
return {
text: "",
editor: ClassicEditor,
editorConfig: {
// The configuration of the editor.
},
}
},
props: {
modelValue: {}
},
setup() {
},
watch: {
modelValue: {
immediate: true,
handler(modelValue) {
this.text = modelValue;
}
},
text(text) {
this.$emit('update:modelValue', text);
}
},
}
</script>
Any suggestions??
I am doing the same tutorial (i am using vueJS-3).
this may work for you:
in app.js include CKEditor:
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => require(`./Pages/${name}.vue`),
setup({ el, app, props, plugin }) {
return createApp({ render: () => h(app, props) })
.use(plugin)
.use( CKEditor)
.mixin({ methods: { route } })
.mount(el);
},
});
In Components/CkEditor.vue check what are you emitting
look for this this.$emit("input", text);
<template>
<ckeditor :editor="editor" v-model="text" :config="editorConfig"></ckeditor>
</template>
<script>
import ClasicEditor from "#ckeditor/ckeditor5-build-classic";
export default {
props: {
value: {},
},
data() {
return {
text: "",
editor: ClasicEditor,
editorConfig: {
// The configuration of the editor.
},
};
},
watch: {
value:{
inmediate: true,
handler(value){
this.text = value;
}
},
text(text) {
this.$emit("input", text);
},
},
};
</script>
let me know if that worked for you
I looked at the answer here, and below is what worked for me:
Hope this helps! :)
I am using laravel/inertia with vue 3.
app.js
import './bootstrap';
import '../css/app.css';
import { createApp, h } from 'vue';
import { createInertiaApp } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m';
import { createPinia } from 'pinia';
import { _t } from './Utilities/translations';
import CKEditor from '#ckeditor/ckeditor5-vue';
const appName =
window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) =>
resolvePageComponent(
`./Pages/${name}.vue`,
import.meta.glob('./Pages/**/*.vue')
),
setup({ el, app, props, plugin }) {
const vue_app = createApp({ render: () => h(app, props) });
vue_app.use(plugin);
vue_app.use(ZiggyVue, Ziggy);
vue_app.use(createPinia());
// Register all base components globally
const components = import.meta.globEager('./Components/Base/*.vue');
for (const path in components) {
let componentName;
if (path.split) {
const split_componentName = path.split('/').pop();
if (split_componentName) {
componentName = split_componentName.replace(/\.\w+$/, '');
vue_app.component(componentName, components[path].default);
}
}
}
vue_app.config.globalProperties.$_t = _t;
vue_app.use(CKEditor);
vue_app.mount(el);
return vue_app;
}
});
InertiaProgress.init({ color: '#4B5563' });
CKEditor Component:
<template>
<div id="app">
<ckeditor
v-model="editor_data"
:editor="editor"
:config="editor_config"
></ckeditor>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from '#vue/reactivity';
import * as editor from '#ckeditor/ckeditor5-build-classic';
const editor_data = ref('');
const editor_config = {};
</script>

Use Worker output in a vue component

I tried to send the output from my worker to my component.vue by window.localStorage.
Does anybody know how to show and update my worker's result in my component vue automatically?
This is my code:
worker-api.js
import Worker from "worker-loader!./worker.js";
const worker = new Worker();
worker.addEventListener('message', (e) => {
window.localStorage.setItem('result', JSON.stringify(e.data));
});
export function sendMessage(msg) {
worker.postMessage(msg);
}
worker.js
self.addEventListener("message", (e) => {
var count = e.data;
while(count < 20) {
const result = e.data + 3
self.postMessage(result);
}
});
my-component.vue
<template>
<p>Count: "{{ result }}"</p>
</template>
<script>
import Button from './Button'
import { sendMessage } from './worker-api'
export default {
name: 'my-component',
components: {Button},
data () {
return {
count : 0
}
},
computed: {
result: function () {
return JSON.parse(window.localStorage.getItem('result'))
}
},
methods: {,
postMessage() {
sendMessage(this.count)
}
},
}
</script>
It is not possible to deal with localStorage values as if they were reactive. Probably, that's why your computed property does not work.
One possible solution is to import your worker inside your component and use to update a reactive variable.
Something similar to:
component.vue
<template>
<button #click="increment">Increment Result</button>
{{ result }}
</template>
<script>
export default {
data() {
return {
// the worker path must be relative to the /public folder (in this example, the worker.js file must be at /public/worker.js)
worker: new Worker('/worker.js'),
result: 0
}
},
created() {
const self = this
this.worker.onmessage = function(event) {
self.result = event.data
}
},
methods: {
increment() {
this.worker.postMessage(this.result)
}
}
}
</script>
/public/worker.js
onmessage = function(event) {
// data sent by the Vue component is retrieved from 'data' attribute
postMessage(event.data + 1)
}

vuejs nextick don't update

i try to connect my frontend to my backend,
the request is done correctly i received the correct data, but the DOM is not updating. I use this.$nextTick but it doesn't affect the update
in the template i use {{ system.CPU.avgload }}
like i said the fetch is done correctly it pass into nexttick, but nothing change
in the main vue i have this
import System from '../utils/system'
import Auth from '../utils/auth'
export default {
created: function () {
this.system = {
CPU: {
avgload: 0
}
}
},
mounted: function () {
this.fetchData()
setInterval(function () {
this.fetchData()
}.bind(this), 10000)
},
methods: {
fetchData () {
if (!Auth.checkAuth) {
console.log('test')
this.error = true
} else {
var self = this
this.$nextTick(function () {
System.Get(function (response) {
self.system = response
})
})
}
}
}
}
and the template is
<div class="text-xs-left" id="example-caption-1">CPU : {{ system.CPU.avgload }} %</div>
You have to add variable system in the data section of vue instance. Than only this variable will become reactive and available in the HTML.
export default {
data: function () {
return { system: {
CPU: {
avgload : ""
}
}
}
}
...
...

How can I use Esri Arcgis Map in ReactJs Project?

I'm trying to use Esri map. To include map in my project, here is what I found:
require([
"esri/map",
"esri/dijit/Search",
"esri/dijit/LocateButton",
"esri/geometry/Point",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",
But there isn't any esri folder or npm package. Therefore, I'm confused here. How esri is imported in project?
Use esri-loader to load the required esri modules. This is a component rendering basemap.
import React, { Component } from 'react';
import { loadModules } from 'esri-loader';
const options = {
url: 'https://js.arcgis.com/4.6/'
};
const styles = {
container: {
height: '100vh',
width: '100vw'
},
mapDiv: {
padding: 0,
margin: 0,
height: '100%',
width: '100%'
},
}
class BaseMap extends Component {
constructor(props) {
super(props);
this.state = {
status: 'loading'
}
}
componentDidMount() {
loadModules(['esri/Map', 'esri/views/MapView'], options)
.then(([Map, MapView]) => {
const map = new Map({ basemap: "streets" });
const view = new MapView({
container: "viewDiv",
map,
zoom: 15,
center: [78.4867, 17.3850]
});
view.then(() => {
this.setState({
map,
view,
status: 'loaded'
});
});
})
}
renderMap() {
if(this.state.status === 'loading') {
return <div>loading</div>;
}
}
render() {
return(
<div style={styles.container}>
<div id='viewDiv' style={ styles.mapDiv } >
{this.renderMap()}
</div>
</div>
)
}
}
export default BaseMap;
This renders a base map but this is not responsive. If I remove the div around the view div or if I give the height and width of the outer div (surrounding viewDiv) as relative ({ height: '100%', width: '100%'}), the map does not render. No idea why. Any suggestions to make it responsive would be appreciated.
An alternative method to the above is the one demonstrated in esri-react-router-example. That application uses a library called esri-loader to lazy load the ArcGIS API only in components/routes where it is needed. Example:
First, install the esri-loader libary:
npm install esri-loader --save
Then import the esri-loader functions in any react module:
import * as esriLoader from 'esri-loader'
Then lazy load the ArcGIS API:
componentDidMount () {
if (!esriLoader.isLoaded()) {
// lazy load the arcgis api
const options = {
// use a specific version instead of latest 4.x
url: '//js.arcgis.com/3.18compact/'
}
esriLoader.bootstrap((err) => {
if (err) {
console.error(err)
}
// now that the arcgis api has loaded, we can create the map
this._createMap()
}, options)
} else {
// arcgis api is already loaded, just create the map
this._createMap()
}
},
Then load and the ArcGIS API's (Dojo) modules that are needed to create a map:
_createMap () {
// get item id from route params or use default
const itemId = this.props.params.itemId || '8e42e164d4174da09f61fe0d3f206641'
// require the map class
esriLoader.dojoRequire(['esri/arcgis/utils'], (arcgisUtils) => {
// create a map at a DOM node in this component
arcgisUtils.createMap(itemId, this.refs.map)
.then((response) => {
// hide the loading indicator
// and show the map title
// NOTE: this will trigger a rerender
this.setState({
mapLoaded: true,
item: response.itemInfo.item
})
})
})
}
The benefit of using esri-loader over the approach shown above is that you don't have to use the Dojo loader and toolchain to load and build your entire application. You can use the React toolchain of your choice (webpack, etc).
This blog post explains how this approach works and compares it to other (similar) approaches used in applications like esri-redux.
You don't need to import esri api like you do for ReactJS. As the react file will finally compile into a js file you need to write the esri parts as it is and mix the ReactJS part for handling the dom node, which is the main purpose of ReactJS.
A sample from the links below is here
define([
'react',
'esri/toolbars/draw',
'esri/geometry/geometryEngine',
'dojo/topic',
'dojo/on',
'helpers/NumFormatter'
], function(
React,
Draw, geomEngine,
topic, on,
format
) {
var fixed = format(3);
var DrawToolWidget = React.createClass({
getInitialState: function() {
return {
startPoint: null,
btnText: 'Draw Line',
distance: 0,
x: 0,
y: 0
};
},
componentDidMount: function() {
this.draw = new Draw(this.props.map);
this.handler = this.draw.on('draw-end', this.onDrawEnd);
this.subscriber = topic.subscribe(
'map-mouse-move', this.mapCoordsUpdate
);
},
componentWillUnMount: function() {
this.handler.remove();
this.subscriber.remove();
},
onDrawEnd: function(e) {
this.draw.deactivate();
this.setState({
startPoint: null,
btnText: 'Draw Line'
});
},
mapCoordsUpdate: function(data) {
this.setState(data);
// not sure I like this conditional check
if (this.state.startPoint) {
this.updateDistance(data);
}
},
updateDistance: function(endPoint) {
var distance = geomEngine.distance(this.state.startPoint, endPoint);
this.setState({ distance: distance });
},
drawLine: function() {
this.setState({ btnText: 'Drawing...' });
this.draw.activate(Draw.POLYLINE);
on.once(this.props.map, 'click', function(e) {
this.setState({ startPoint: e.mapPoint });
// soo hacky, but Draw.LINE interaction is odd to use
on.once(this.props.map, 'click', function() {
this.onDrawEnd();
}.bind(this));
}.bind(this))
},
render: function() {
return (
<div className='well'>
<button className='btn btn-primary' onClick={this.drawLine}>
{this.state.btnText}
</button>
<hr />
<p>
<label>Distance: {fixed(this.state.distance)}</label>
</p>
</div>
);
}
});
return DrawToolWidget;
});
Below are the links where you can find information in detail.
http://odoe.net/blog/esrijs-reactjs/
https://geonet.esri.com/people/odoe/blog/2015/04/01/esrijs-with-reactjs-updated

Resources