Vue 3 does not update getter - vue.js

I am in the process of updating vue2 to vue3 but encounter this problem.
I have a service called TService
// T.ts
class T {
public obj = { value: false };
constructor() {
setInterval(() => {
this.obj.value = !this.obj.value;
}, 1000);
}
}
const t = new T();
export { t as TService };
The service is very simple, it update it's obj value every 1 second.
Now come to the fun part
On vue2, I can do this:
<template>
<div> {{ test }} </div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
import { TService } from './T;
#Component
export default class HelloWorld extends Vue {
public obj = TService.obj;
get test() {
return this.obj.value;
}
}
</script>
The test value updated on screen every 1sec and works as expected.
However, when I changed to vue3 with the below code, it does not work any more
<template>
<div>{{ test }}</div>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import { TService } from './T';
#Options({})
export default class HelloWorld extends Vue {
public obj = TService.obj;
get test() {
return this.obj.value;
}
}
</script>
Not sure what is going on and appreciate if anyone can fix my code.
I am using latest vue 3.1.5 and vue-class-component 8.0.0-rc.1

You probably should make it reactive so Vue knows its value can be updated, see here

The anwser can be found in this post:
Changes made to an object created outside of Vue component are not detected by Vue 3
Basically I will need to wrap reactive around my object in my service
import { reactive } from 'vue';
// T.ts
class T {
public obj = reactive({ value: false });
constructor() {
setInterval(() => {
this.obj.value = !this.obj.value;
}, 1000);
}
}
const t = new T();
export { t as TService };

Related

is it available to call the methods where in the vue component from the plugin?

I wanted to access the vue.data or methods in the plugin.
no matter what I tried several times, it didn't work.
such as eventBus, Mixin etc...
so I'm curious about the possibility to call the methods like that.
thank you for reading this question.
here is the custom component.
<template>
<div>
<v-overlay :value="isProcessing">
<v-progress-circular indeterminate size="64"></v-progress-circular>
</v-overlay>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
#Component
export default class ProgressCircular extends Vue {
private isProcessing: boolean;
startProcess() {
this.isProcessing = true;
}
}
</script>
and this is the plugin source.
import ProgressCircular from '#/components/ProgressCircular.vue';
import { VueConstructor } from 'vue';
import Vuetify from 'vuetify/lib';
import vuetify from './vuetify';
export default {
install(Vue: VueConstructor, options: any = {}) {
Vue.use(Vuetify);
options.vuetify = vuetify;
Vue.component('progress-circular', ProgressCircular);
Vue.prototype.$fireProgressing = function () {
// it didn't work
// I just wanted to access the method where in the Vue Component
// ProgressCircular.startProcess();
};
},
};
use the plugin syntax to extend vue like:
Vue.use({
install: Vue => {
Vue.prototype.$fireProgressing = () => {
};
}
});
or
Vue.use(YOURPLUGIN);
before you mount vue

this.$forceUpdate equivalent in Vue 3 - Composition API?

In Vue 2, instance method this.$forceUpdate() could be used to update the component manually. How can we force update component in Vue 3 - Composition API (inside setup method) ?
setup(props, context) {
const doSomething = () => {
/* how to call $forceUpdate here? */
}
return {
doSomething
}
}
Thanks, in advance.
If using Options API:
<script lang="ts">
import {getCurrentInstance, defineComponent} from 'vue'
export default defineComponent({
setup() {
const instance = getCurrentInstance();
instance?.proxy?.$forceUpdate();
}
})
</script>
If using Composition API with <script setup>
<script setup lang="ts">
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance();
instance?.proxy?.$forceUpdate();
</script>
When I need to force an update in vue I usually add a key with a value I can change, which will then force vue to update it. That should work in vue 3 as well, though I admit I haven't ever tried it. Here's an example:
<template>
<ComponentToUpdate
:key="updateKey"
/>
</template>
<script>
export default {
data() {
return {
updateKey: 0,
};
},
methods: {
forceUpdate() {
this.updateKey += 1;
}
}
}
</script>
You can read more about it here: https://michaelnthiessen.com/key-changing-technique/
$forceUpdate is still available in Vue3, but you won't have access to it in the setup() function. If you absolutely need to use it, you can use object API component or this fancy trick...
app.component("my-component", {
template: `...`,
methods: {
forceUpdate(){
this.$forceUpdate()
}
},
setup(props) {
const instance = Vue.getCurrentInstance();
Vue.onBeforeMount(()=>{
// instance.ctx is not populated immediately
instance.ctx.forceUpdate();
})
return {doSomething};
},
})
If this seems like a ridiculous solution, trust your Judgement. Ideally your application would not rely on forceUpdate. If you are relying on it, it likely means that something is miss-configured, and that should be the first thing to resolve.

vue3 class component access props

i use vue3 with class-component in typescript my class looks like:
import {Options, Vue} from "vue-class-component";
#Options({
props: {
result: Object
}
})
export default class imageResult extends Vue {
currentImage = 0;
getSlides(){
console.log('result',this.$props.result); // not working
console.log('result',this.result); // not working too
}
My question is, how can i access and use the property within my class?
both this.result and this.$props.result throws me an error.
can someone help me?
Thanks in advance
late answer but maybe it helps someone in the future.
works for me with vu3 & typescript
<script lang="ts">
import { Vue, prop } from "vue-class-component";
export class Props {
result = prop<string>({ required: true });
}
export default class Foo extends Vue.with(Props) {
test(): void {
console.log(this.result);
}
}
</script>
My suggestion to you is to follow the documentation on using Typescript with vue using class component: enter link description here
in order to fix your code I think this should work:
import {Vue} from "vue-class-component";
import {Component} from "vue-class-component";
// Define the props by using Vue's canonical way.
const ImageProps = Vue.extend({
props: {
result: Object
}
})
// Use defined props by extending GreetingProps.
#Component
export default class ImageResult extends ImageProps {
get result(): string {
console.log(this.result);
// this.result will be typed
return this.result;
}
}

Vue.js infinite loop with prop array

In my App.vue:
I'm using updated() for getting user info after callback and each time I change the route and getting info and
mounted(), if I used F5 or go to my website already connected with SSO
The problem is that in my updated(), I set an array which is passed to a component. It make infinity loop.
How can I avoid it ?
App.vue :
<template>
<Sidebar v-if="isLoggedIn" :displayName="currentUser" v-bind:role="rolesT" />
</template>
<script lang="ts">
public rolesT: ReadonlyArray<String> = ["test"];
public initUser() {
this.auth = Vue.prototype.$auth;
this.auth.getUser().then((user) => {
if (user !== null) {
this.currentUser = user.profile.name!;
this.accessTokenExpired = user.expired;
this.rolesT = []; // if I use only this one, I have infinite loop
this.rolesT = user.profile.roles ; // if I use only this one, I have infinite loop
this.rolesT = this.rolesT; // if I use only this one, I dont have infinite loop, but its useless, it was just for test
console.log(this.rolesT);
}
this.isLoggedIn = (user !== null && !user.expired);
});
}
public mounted() {
console.log("mounted");
this.initUser();
}
public updated() {
console.log("updated");
this.initUser();
}
}
</script>
Sidebar.vue (I do nothing with the array for now, but still loop) :
[...]
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
#Component
export default class Sidebar extends Vue {
#Prop({default: 'Unknow name'})
private displayName!: string;
#Prop({default: 'Unknow username'})
private username!: string;
#Prop()
private role;
}
</script>

Aurelia - Watch Dependency Value for Change

Suppose you have a class you are injecting into a another class or component. Is there a way to watch for changes on an attributed of the dependency you are injecting and act upon it?
For example, say you have the following app:
app.html
<template>
<input type="text" value.bind="item">
<button click.trigger="addToList()">Add</button>
<h3>Modded</h3>
<ul>
<li repeat.for="it of modded">${it}</li>
</ul>
<h3>Original</h3>
<ul>
<li repeat.for="it of dep.items">${it}</li>
</ul>
</template>
app.js
import {bindable, inject} from 'aurelia-framework';
import {Dep} from './dep';
#inject(Dep)
export class App {
constructor(dep) {
this.dep = dep;
}
attached() {
this.modifyItems();
}
addToList() {
this.dep.addItem(this.item);
}
modifyItems() {
this.modded = [];
for (let item of this.dep.items) {
this.modded.push(item.toUpperCase());
}
}
}
dep.js
export class Dep {
constructor() {
this.items = ['one', 'two', 'three'];
}
addItem(item) {
this.items.push(item);
}
}
Now, let's say that some other component modifies Dep.items. Is there a way to watch for changes in app.js on this.dep.items and then call modifyItems()?
Assume modifyItems() is more complex than this example so maybe a value converter is not the best option. (unless it is the only option I guess)
Here is working plunker with the above example: http://plnkr.co/edit/rEs9UM?p=preview
Someone pointed me to the BindingEngine.collectionObserver and it appears that is what I needed.
app.js:
import {inject} from 'aurelia-framework';
import {BindingEngine} from 'aurelia-binding';
import {Dep} from './dep';
#inject(Dep, BindingEngine)
export class App {
constructor(dep, bindingEngine) {
this.dep = dep;
let subscription = bindingEngine.collectionObserver(this.dep.items)
.subscribe((newVal, oldVal) => {
console.debug(newVal, oldVal);
this.modifyItems();
});
}
attached() {
this.modifyItems();
}
addToList() {
this.dep.addItem(this.item);
this.item = '';
}
modifyItems() {
this.modded = [];
for (let item of this.dep.items) {
this.modded.push(item.toUpperCase());
}
}
}
Here is the working pluker: http://plnkr.co/edit/Pcyxrh?p=preview