vue watch can detect external js file attributes? - javascript

An external js file that changes the value of a property by a click
event, but how does it detect that this property has changed since the
vue page used this property value?
1.External js
var obj = {},initValue = true;
Object.defineProperty(obj, "newKey", {
get: function () {
return initValue;
},
set: function (value) {
return initValue = value;
}
});
var con=function () {
document.getElementById('btncc').onclick = function () {
setTimeout(() => {
console.log(obj.newKey)
}, 3000)
initValue = false;
}
}
export default {
con,obj
}
> 2.vue page
> Change the style
<input id="color" type="color" :class="obj ? 'transparent': 'transparent2'" :disabled ="obj"/>
import Canvas from '../../Api/js'
export default {
name: 'HelloWorld',
data () {
return {
obj:Canvas.obj.newKey
}
},
mounted () {
Canvas.con()
console.log(this.obj,'我是msg啊');
},
watch: {
[Canvas.obj.newKey] (val) {
if(val){
console.log(this.obj,'我改变了吗,');
}
}
}
}
watch no effect.
Probably like this.
https://jsfiddle.net/qjwanglei/L5afz75t/3/

Related

Vue Composable Functions & Monaco Editor

I'm trying to abstract some repetitive functions to update parts of the Monaco edit from various check boxs in the UI. The example below enables or disables linenumber in the editor.
Works fine when the editor and functions are in the same setup. When I abstract to a composable per below the editor is null, therefore does not work. Any thoughts or ideas?
Note: Some code is removed for brevity
MainTemplate.vue
import * as monaco from 'monaco-editor';
import useCodeEditor from '../composables/codeEditorFunctions';
import { ref, onMounted, reactive, inject, watch } from 'vue';
export default {
setup(props, { emit }) {
let meditor = null;
onMounted(() => {
checkDarkModeIsSet();
checkLineNumbersIsSet();
checkMiniMapIsSet();
const codeEditorDiv = document.getElementById('pf-c-code-editor__code-pre');
meditor = monaco.editor.create(codeEditorDiv, {
value: ['function x() {', '\tconsole.log("If you see this, something went wrong!");', '}'].join('\n'),
language: 'yaml',
lineNumbers: lineNumbers.value,
roundedSelection: false,
scrollBeyondLastLine: true,
readOnly: false,
theme: darkmode.value,
scrollBeyondLastLine: false,
automaticLayout: true,
wordWrap: 'on',
wrappingStrategy: 'advanced',
minimap: {
enabled: true
}
});
if (props.viewstate.editid === 0) {
getDefaultCodeBlock();
} else {
showCodeBlock();
}
getModel(props.viewstate.editid);
});
const { darkmode, checkDarkModeIsSet, toggleEditorDarkMode, lineNumbers, checkLineNumbersIsSet, toggleEditorLineNumbers } = useCodeEditor(monaco, meditor);
return {
darkmode,
lineNumbers,
toggleEditorDarkMode,
toggleEditorLineNumbers,
toggleEditorMinimap,
showConfigFullScreen
};
}
};
codeEditorFunctions.js
import { ref, reactive } from "vue";
export default function useCodeEditor(monaco, meditor) {
/** EDITOR DARKMODE */
const darkmode = ref('vs');
function checkDarkModeIsSet() {
if (localStorage.getItem('editordarkmode') === null) {
darkmode.value = 'vs';
localStorage.setItem('editordarkmode', darkmode.value);
} else {
darkmode.value = localStorage.getItem('editordarkmode');
}
}
function toggleEditorDarkMode(event) {
if (event.target.checked) {
darkmode.value = 'vs-dark';
localStorage.setItem('editordarkmode', darkmode.value);
} else {
darkmode.value = 'vs';
localStorage.setItem('editordarkmode', darkmode.value);
}
monaco.editor.setTheme(darkmode.value);
}
/** EDITOR LINNUMBERS */
const lineNumbers = ref('on');
function checkLineNumbersIsSet() {
if (localStorage.getItem('editorlineNumbers') === null) {
lineNumbers.value = 'on';
localStorage.setItem('editorlineNumbers', lineNumbers.value);
} else {
lineNumbers.value = localStorage.getItem('editorlineNumbers');
}
}
function toggleEditorLineNumbers(event) {
if (event.target.checked) {
lineNumbers.value = 'on';
localStorage.setItem('editorlineNumbers', lineNumbers.value);
} else {
lineNumbers.value = 'off';
localStorage.setItem('editorlineNumbers', lineNumbers.value);
}
meditor.updateOptions({
lineNumbers: lineNumbers.value
});
}
return {
darkmode,
checkDarkModeIsSet,
toggleEditorDarkMode,
lineNumbers,
checkLineNumbersIsSet,
toggleEditorLineNumbers,
};
};
Basically, function toggleEditorLineNumbers(event) throws an error becuase meditor (meditor.updateOptions) is null.
ANy ideas?
This is the case for a ref, which basically is a recipe that allows to pass arbitrary value by reference. Since meditor isn't supposed to be used prior to useCodeEditor call, it makes more sense to define it inside a composable instead of providing it as an argument:
function useCodeEditor(monaco) {
const meditor = ref(null);
...
return {
meditor,
...
}
}

Best practice for implementing custom form in Vue

My goal is to create a custom form component called app-form which provides v-model to access the validation. For the input I want to detect is also a custom component called app-input.
Here is my implementation so far.
app-form
<template>
<div>
<slot></slot>
</div>
</template>
const acceptedTags = ['app-input'];
export default {
/*
props: value,
data: isValid
*/
updated() {
// update isValid whenever the view changes
this.checkValidation();
},
methods: {
checkValidation() {
this.isValid = true;
this.checkValidation(this);
this.$emit('input', this.isValid);
},
checkValidationRecursion(node) {
if (acceptedTags.includes(node.$options.name)) {
let result = node.checkValidation();
this.isValid &&= result;
}
node.$children.forEach((child) => this.checkValidationRecursion(child));
},
}
}
app-input
<input
:value="selfValue"
#input="onInput"/>
export default {
name: 'app-input',
/*
props: value,
data: selfValue,
*/
methods: {
checkValidation() {
let valid = true;
/*
this.rules = [(v) => !!v || 'Required'];
*/
for (let f of this.rules) {
let result = f(this.selfValue);
if (typeof result === 'string') {
valid = false;
}
}
return valid;
},
// onInput() => update selfValue and emit
},
// watch: update selfValue
}
In the code above, the app-form have to traverse the whole component tree to get the target inputs every time anything is updated. Is there any better way to achieve it?
For these Kind of things I like to use provide/inject https://v2.vuejs.org/v2/api/#provide-inject. The idea is to "provide" an object in a Parent-Component (your Form-Component) and to "inject" this object in any of your descandant Components. Data here is not reactive, but you can pass a reference to a reactive Object (for example a Vue Object).
If providing a Vue-Instance you can emit an event, like "check-validation" on that instance and your descandant components can listen to that and then emitting an validate-Event with the validation-Result back to the parent Component.
Here is a very basic Example: https://codepen.io/arossbach/pen/xxdxOVZ
Vue.component('my-form', {
provide () {
return {
formBus: this.formBus,
};
},
mounted() {
setTimeout(() => {
this.formBus.$emit("check-validation");
},4000);
this.formBus.$on("validate-element", isValid => {
this.isValidForm &&= isValid;
});
},
data () {
return {
formBus: new Vue(),
isValidForm: true,
};
},
template: '<div><slot></slot></div>',
});
Vue.component('child', {
inject: ['formBus'],
template: '<div></div>',
data() {
return {
isValid: true,
}
},
methods: {
validate() {
this.isValid = Boolean(Math.round(Math.random()));
this.formBus.$emit("validate-element", this.isValid);
}
},
created() {
this.formBus.$on("check-validation", this.validate);
}
});
new Vue({
el: '#app',
data () {
return {
};
},
});
HTML:
<div id="app">
<my-form>
<child></child>
<child></child>
</my-form>
</div>

Function not recognized by the new Vue Composition API

We're trying to convert a simple SFC to use the new Vue CompositionAPI. This code works flawless:
export default {
data() {
return {
miniState: true
}
},
methods: {
setMiniState(state) {
if (this.$q.screen.width > 1023) {
this.miniState = false;
} else if (state !== void 0) {
this.miniState = state === true
}
else {
this.miniState = true
}
},
},
watch: {
'$q.screen.width'() {
this.setMiniState()
}
}
};
Converting this to the new CompostionAPI looks like this:
export default defineComponent({
setup() {
const miniState = ref(true)
const setMiniState = (state) => {
if ($q.screen.width > 1023) {
miniState.value = false
} else if (state !== void 0) {
miniState.value = state === true
}
else {
miniState.value = true
}
}
watch('$q.screen.width'(),
setMiniState()
)
return {
miniState, setMiniState
}
}
})
However, every time I try to run this Vue complains that $q.screen.width is not a function. What am I doing wrong here?
You're calling $q.screen.width instead of adding it as a watch source.
Try this:
watch('$q.screen.width', (newVal, oldVal) => setMiniState())

How to get the parent function name of the function being called

I am trying to get the name of the parent function of the function being called.
For example if I have these functions:
var functions = {
coolfunction1: {
add: function () {
},
delete: function () {
},
save: function () {
}
},
coolfunction2: {
add: function () {
// i want to console.log() the name of the parent of this function,
// output: coolfunction2
},
delete: function () {
},
save: function () {
}
}
}
When I call functions.coolfunction2.add(), is there a way to log the name of the parent function that was run?
I know I can use the variable this but that only outputs the names of the children functions, add(), delete(), save().
How can I know that the coolfuntion2 was run?
I know this can be done manually, by rewriting the function name in the add() function, but is there a way to get the name dynamically?
You can add a getter to those methods as
Object.keys(functions).forEach(t =>
Object.keys(functions[t]).forEach(t2 => {
var func = functions[t][t2]; //save a reference to function since it won't be a function anymore once a getter is assigned
Object.defineProperty(functions[t], t2, {
get: function() {
console.log(t); //print the name of parent property or grand-parent property, etc
//func();
return func; //return the reference to this function
}
});
})
);
Demo
var functions = {
coolfunction1: {
add: function() {
},
delete: function() {
},
save: function() {
}
},
coolfunction2: {
add: function() {
console.log("a is invoked");
},
delete: function() {
},
save: function() {
}
}
};
Object.keys(functions).forEach(t =>
Object.keys(functions[t]).forEach(t2 => {
var func = functions[t][t2];
Object.defineProperty(functions[t], t2, {
get: function() {
console.log(t);
//func();
return func;
}
});
})
);
functions.coolfunction2.add();
functions.coolfunction2.add();
functions.coolfunction1.add();

Javascript optional parameters for callbacks

I want to do something like $.ajax() success and error callbacks.
This is what I have so far:
var FileManager = {
LoadRequiredFiles: function (onLoadingCallback, onCompleteCallback) {
//Not sure what to do here
this.OnLoading = onLoadingCallback;
this.OnCompleteCallback = onCompleteCallback;
this.OnLoading();
this.OnComplete();
},
OnLoading: function () {
//empty by default
}
OnComplete: function () {
//empty by default
}
};
//I want to do something like this:
FileManager.LoadRequiredFiles({OnLoading: function() {
alert('loading');
}
});
How do I fix this up properly? I'm using FileManager as my namespace.
You can check if the functions are defined:
var FileManager = {
LoadRequiredFiles: function (config) {
config = config || {};
this.OnLoading = config.onLoadingCallback;
this.OnCompleteCallback = config.onCompleteCallback;
if(typeof this.OnLoading =='function') {
this.OnLoading();
}
//Or use the shortcut:
if(this.OnComplete) {
this.OnComplete();
}
}
};
FileManager.LoadRequiredFiles(
{
onLoadingCallback: function() {
alert('loading');
}
}
);

Categories

Resources