I have a template like this:
<template>
<div>{{hello}}</div>
</template>
<script>
export default {
data() {
hello: "Hello World!",
secret: "The answer is 42"
}
}
</script>
How can I get, and manipulate the value of secret from the Chrome debugging console?
I have installed the Vue extension which allows me to look at my component and see the variables. What I would like to do is to get these variables and manipulate them.
Is this possible?
There is a similar discussion on the Vue.js forum. Specifically, SevenLines post on Mar 25 2018 reads:
Update from 2018. If you use vue-devtools, you can access Vue from
console by selecting desired component, and then accessing it through
$vm variable. Check the image with example:
I use this
// if result is single element
document.getElementById('app').__vue__
// if result is multiple elements
document.getElementsByClassName('some-class')[0].__vue__
assuming your using id #app for your entry
can also do it in other elements as long as identified as Vue Component
element
and get element by tag/id/class
Here's what I put in the Console, and it immediately modifies the data shown on the page:
myapp = app.__vue__.$children[0]
myapp.hello = 'Bonjour'
myapp.hello = 'Buenos dias'
Mysteriously, if you skip assigning the object to a variable, it doesn't work as intended:
app.__vue__.$children[0].hello = 'Bonjour' // first time it works
app.__vue__.$children[0].hello = 'Buenos dias' // second time it doesn't work
Related
I am adding some DOM elements in the script setup side but I want the messages to change when I change the language. I am using vue-i18n plugin. It's easy to do it in the template section because I can basically use the useI18n().t method but how can I do this in the script setup section. Using the useI18n().t method doesn't ensure reactivity.
Example Code:
$(".time")[0].innerHTML = `
<div>0<span>${useI18n().t("details.hour")}</span></div>
<div>0<span>${useI18n().t("details.minute")}</span></div>
<div>0<span>${useI18n().t("details.second")}</span></div>
`
Manipulating DOM directly inside the script leads to inconsistence in your app, you should drive your component by different reactive data to achieve your goal.
In your current situation try to define a computed property based on the translation then render it inside the template based on its different properties :
<script setup>
const {t} =useI18n()
const time = computed(()=>{
return {
hour:t(""details.hour"),
minute:t(""details.minute"),
second:t(""details.second"),
}
})
</script>
<template>
<div class="time">
<div>0<span>{{time.hour}}</span></div>
<div>0<span>{{time.minute}}</span></div>
<div>0<span>{{time.second}}</span></div>
</div>
</template>
How would I access variables declared within the setup script within the none setup script? I attempt to update the message in the example below when something happens within the script tags. However, msg just returns undefined, I've looked through the Vue documentation on setup scrips, but there doesn't seem to be any mention of accessing the variable in none setup scripts.
<script setup>
var msg = 'Hello!';
</script>
<script>
export default {
data() {
return {
};
},
mounted(){
// my current attempt to get msg and update it. However, I just get undefined.
this.msg = 'Something happened inside script tags';
//or msg = 'Something happened inside script tags';
},
</script>
<template>
<p>{{ msg }}</p>
</template>
Seems like you are mixing up two syntaxes here - the newer, Composition API (Vue3, setup script) and Options API (Vue2 style, one with data and mounted functions). While it's still ok to use the Options API syntax, you should only use one of the two. I suggest reading up on the different syntaxes:
Composition API
Options API
The tag is syntactic sugar for THIS type of syntax. Everything returned by the setup() function will be made available to the template. If you're using the tag, by default everything is made available.
For the Options API syntax, everything returned by the data function is made available to the template. Delete the entire first tag and try adding the "msg" property to the object returned by the data function and see if it works.
data() {
return {
msg: ''
};
}
Following this tutorial, I'm trying to programmatically create instances of a component on my page.
The main snippet is this:
import Button from 'Button.vue'
import Vue from 'vue'
var ComponentClass = Vue.extend(Button)
var instance = new ComponentClass()
instance.$mount()
this.$refs.container.appendChild(instance.$el)
However I get two errors:
The component I'm trying to instantiate contains references to the store, and these don't work: "TypeError: Cannot read property 'state' of undefined".
For the last line of the snippet (this.$refs.container.appendChild(instance.$el)) I get this error: "Uncaught TypeError: Cannot read property 'container' of undefined"
I'm really not sure how to troubleshoot this, if anyone strong in Vue.js could give me some hint as to why I'm getting these errors and to solve them that would be terrific.
1) Since you're manually instantiating that component and it doesn't belong to your main app's component tree, the store won't be automatically injected into it from your root component. You'll have to manually provide the store to the constructor when you instantiate the component ..
import ProjectRow from "./ProjectRow.vue";
import Vue from "vue";
import store from "../store";
let ProjectRowClass = Vue.extend(ProjectRow);
let ProjectRowInstance = new ProjectRowClass({ store });
2) In a Vue Single File Component (SFC), outside of the default export this doesn't refer to the Vue instance, so you don't have access to $refs or any other Vue instance property/method. To gain access to the Vue instance you'll need to move this line this.$refs.container.appendChild(instance.$el) somewhere inside the default export, for example in the mounted hook or inside one of your methods.
See this CodeSandbox for an example of how you may go about this.
This is another way to instantiate a component in Vue.js, you can use two different root elements.
// Instantiate you main app
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
//
// Then instantiate your component dynamically
//
// Create a component or import it.
const Hello = {
props: ['text'],
template: '<div class="hello">{{ text }}</div>',
};
// Create a componentClass by Vue.
const HelloCtor = Vue.extend(Hello);
// Use componentClass to instantiate your component.
const vm = new HelloCtor({
propsData: {
text: 'HI :)'
}
})
// then mount it to an element.
.$mount('#mount');
It works by assigning "this" to the property "parent". By setting the parent you also have access to the $store in the new instance. (Provided that "this" is another Vue instance/Component and already has access to the store, of course)
new (Vue.extend(YourNewComponent))({
parent: this,
propsData: {
whatever: 'some value',
},
}).$mount(el.querySelector('.some-id'))
If you don't need the reference to the parent, you can just leave "parent: this," out.
Important note: When mounting many (like 500+) items on the page this way you will get a huge performance hit. It is better to only give the new Component the necessary stuff via props instead of giving it the entire "this" object.
I went down this path, following all the examples above, and even this one: https://css-tricks.com/creating-vue-js-component-instances-programmatically/
While I got far, and it works (I made a lot of components this way), at least for my case, it came with drawbacks. For example I'm using Vuetify at the same time, and the dynamically added components didn't belong to the outer form, which meant that while local (per component) validation worked, the form didn't receive the overall status. Another thing that did not work was to disable the form. With more work, passing the form as parent property, some of that got working, but what about removing components. That didn't go well. While they were invisible, they were not really removed (memory leak).
So I changed to use render functions. It is actually much easier, well documented (both Vue 2 and Vue 3), and everything just works. I also had good help from this project: https://koumoul-dev.github.io/vuetify-jsonschema-form/latest/
Basically, to add a function dynamically, just implement the render() function instead of using a template. Works a bit like React. You can implement any logic in here to choose the tag, the options, everything. Just return that, and Vue will build the shadow-DOM and keep the real DOM up to date.
The methods in here seems to manipulate the DOM directly, which I'm glad I no longer have to do.
I'm doing a project with ElementUI Tabs (just HTML and JS files, no .vue files) and I want to open a new Tab, and add html inside, like I've always used to do in Jquery and SemanticUI, for example, the user clicks the menu called "Person" and the Person View (a Vue component) opens in the tab (id = "tab1") to add a new person register, and if the user clicks again the "Person" menu, another tab opens (id = "tab2") with the Person View.
First Question: Because the Vue Component has no "el:" selector, how can I tell to component (Person View) to open inside the "tab1", and another click to open inside the "tab2" ? There is any selector like "el" in Vue.component()?
Second Question: Using Vue instance ( new Vue ({options}) ), it works, because is possible to use the selector "el", but I've read before in some blogs, that is not good practice, because the app must have only one instance of Vue. Is correct add more than one Vue instance ( new Vue () ) as used to be done adding many Vue.component ({}) in the project?
Third Question: I've read before that Vue.component() is a Vue instance, and so would be correct to say that Vue.component() and Vue() is the same thing, but with different sintax ?
Question 1:
Actually, a component does have an el. Your template determines what el is.
For example, I created an inline template for my select2 that look like this:
<select2>
<select></select>
</select2>
Vue.componet("select2", {blah blah blah});
in this case el is the select box directly.
If I did:
<select2>
<div>
<select></select>
</div>
</select2>
the component el would be the div.
Question 2: what you heard from those blogs is nonsense, at least as far as Vue 2 is concerned (never worked with ver 1)
You, as a coder, determine what el is in your code so it is safe to use as a selector. I do it all of the time.
Vues cannot overlap but you can have as many on a page as makes sense. On one set of my tabs, each tab is completely different from each other and independent of each other so each has its own Vue instance. On another, each tab is the same so a made a single component and generated it inside each tab as part of the parent Vue instance.
question 3:
Think of Components as parts and the Vue instance as the whole containing the parts. I personally use components to reduce and compartmentalize code. For example, I have a DataTables component, a select2 component and a tab component, in all cases I have a number of each on each page. Then all I need to do is include them in my Vue instance definition.
After almost two weeks trying, I got it !
First i created an object that has a component structure in a JS file
(personview.js) that i load with requireJS, and pass as a parameter to a
method of Vue Instance called appVue:
appVue.addComponent(componentName,{name:"personview",template:"<div>html tags...</div>",methods:...});
In appVue i added the method:
var appVue=new Vue({
el:'#app',
data() {
return {
components: {},
instances: {}
}
},
methods: {
addComponent(componentName,componentBody){
this.$data.components[componentName]=Vue.component(componentName,Vue.extend(componentBody));
}
}
}
When the user clicks on menu, the method openViewByClickOnMenu is called
and executes:
methods: {
openViewByClickOnMenu(){
//id to identify the components and scripts to load
var componentName="personView"; //for this example i forced the name
//call a method that adds the new tab, and inside the tab adds
//<div id="divX"></div> and return the counter ever increased.
//X in id attribute is the number genereate by the counter
var ctTab=body.addTab({label:menuItem.label});
// will be used to identify an instance of compoment
var componentId=componentName+ctTab; //will be personView1, personView2, etc..
// will be used to identify the div where i want to show component
var divTabId="div"+ctTab;
//load the personview.js with component body
requirejs([componentName],function(){
//creates a new instance of component
app.$data.instances[componentId]=new app.$data.componentes[componentName];
//mounts the component in the div that i want
app.$data.instances[componentId].$mount("#"+divTabId);
});
}
I think the Vue team could add a method in Vue instance to add
components dinamically more easily, sometimes there's no need to
load all html and js files because the user has no acess/permissions
to see some views. And i miss a way to load html native, like
Angular does, because sometimes we need generate html from template engine
inside a SpringBoot for example.
I'd like to use Vue with pre-rendered HTML. In the long run we might not, but for now it seems easier to leave the server code in place and use Vue to manage the client logic of the web app.
I can't seem to find anything that describes how to have the view model load its data from the HTML. My example will be the same as https://github.com/vuejs/Discussion/issues/56. Even though that issue was closed (and fixed?), I can't seem to get that to work in either 1.x or 2.x.
When I set up some HTML
<div id="myvue">
<span v-text="message">Hello!</span>
</div>
and then create a view-model for it
var vm = new Vue({
el: '#myvue'
});
I'm hoping to have the vm object hydrated with data from the HTML so that vm.message == 'Hello!' is true. But that doesn't happen. I just end up with an error saying that message is not defined with version 1. With version 2 I get a warning that says Property or method "message" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
But when I declare the data property in the vm like so
var myvue = new Vue({
el: '#myvue',
data: {
message: <something>
}
});
no matter what <something> is (null, a string, undefined), it always replaces what's in the HTML element. Is there any way to have the Vue object load data from the HTML?
It is theoretically impossible to hydrate your state directly from HTML, because rendering is not a bijective function. Two simple examples:
<p>{{ foo }}</p> renders to <p>2</p>. Is foo a string or a number?
<p>{{ foo + bar }}</p> renders to <p>6</p>. What are the values of foo and bar?
The simplest way to hydrate your state is to take the data you use for rendering on the server, serialize it to JSON and render it using <script>window.hydrationData = ...</script> directly in your document. This way, you can use it for data when you create the Vue instance.
This approach is also used in the official Vue demo app.
It's not related to Vue, but Knockout.js can kind of do this when used with ErikSchierboom's knockout-pre-rendered custom binding.
This combo essentially lets you define an input or something
<div id="mydiv">
<input data-bind="init, textInput: content" value="foo"/>
</div>
And then bind a model object to it
function ViewModel() {
this.content = ko.observable();
}
var vm = new ViewModel();
ko.applyBindings(vm, $('#mydiv')[0])
With the init binding on the input, the vm content gets loaded with the value from the input when it gets bound.
Again, it's not Vue. But thought I'd put it up incase it helps someone.
One option, if your rendered HTML doesn't depend on computed properties, might be to parse your generated HTML to grab the values you need for your data (say using jQuery or vanilla-js to create a data object to pass to your VM when you instantiate it:
$(function(){
var app = '#app';
var $app = $(app);
var data = {
a: 0, // some initial value not present in HTML
b: $app.find('.foo').text(),
c: $app.find('input[name=c]').val()
};
var vm = new Vue({
el: app,
template: '...template...', // if needed
data: data,
...
});
});
When instantiated, the data model should match the rendered HTML. You may need to supply the template if not all template components are part for the rendered HTML. Note that this most likely will cause the app to re-render once instantiated. You might be able to add the server-rendered="true" attribute to your root app element to prevent this if the pre-rendered HTML is identical to what the vm would render.
Another, not so automated option would be to have your template generate a <script> tag just below your element which the data model in it:
<script>
window.MYSTATE = window.MYSTATE || {};
window.MYSTATE['#app'] = {
"a": 0,
"b": "Some text",
"c": "Some value"
};
</script>
Make sure your data is properly escaped into JSON format. Then in your page scripts where you instantiate your app , use the window.MYSTATE['#app'] as the data object (doing a deep clone to prevent changes in the window.MYSTATE instance affecting your model.
var vm = new Vue({
el: '#app',
template: '...template if needed...',
data: $.extend(true, {}, window.MYSTATE['#app'])
...
});
Just make sure you do the instantiation after the page has loaded (i.e. by placing this at the bottom of the page before the closing body tag)