Vuejs, how to put data in children component? - vue.js

I want to set currentView variable if changePassword method start.
I tried $broadcast, if the event is in Model var it works, but if I put the event in Setting var, $broadcast does not work. and I tried $children too, but didn't work.
var Settings = Vue.extend({
template: '#temp-setting',
data: function() {
return { currentView: '' };
},
components: {
'change-password': ChangePassword
}
});
This is full vuejs code
var ChangePassword = Vue.extend({
template: '#temp-change-password'
});
var Settings = Vue.extend({
template: '#temp-setting',
data: function() {
return { currentView: '' };
},
components: {
'change-password': ChangePassword
}
});
var Modal = Vue.extend({
template: '#temp-modal',
methods: {
close: function() {
Router.go('/');
$('#modal').modal('hide');
}
}
});
var MainApp = Vue.extend({
methods: {
changePassword: function() {
Router.go('/settings/change-password');
$('#modal').modal({
backdrop: false,
keyboard: false
});
}
},
ready: function() {
Router.go('/');
}
});
var Router = new VueRouter();
Router.map({
'/': {
component: Modal,
},
'/settings': {
component: Modal,
subRoutes: {
'/change-password': {
component: Settings
}
}
}
});
Router.start(MainApp, 'body');

Related

Vue redirect after getting store's values

I was trying to make an edit page /edit/:id where input's value comes from the vuex store, but if the store doesn't have a task with the same id of the params I want to redirect to /404, how can I achieve this? tried created() but it didn't work. Im using Vue3
<script>
export default {
name: "EditTaskForm",
data() {
return {
newTaskDesc: "",
};
},
created() {
const task = this.$store.getters.getTaskById(Number(this.$route.params.id));
if (!task) this.$router.push("/404");
},
mounted() {
const task = this.$store.getters.getTaskById(Number(this.$route.params.id));
this.newTaskDesc = task.desc;
},
methods: {
submitEdit() {
this.$store.dispatch("editTask", {
newTaskDesc: this.newTaskDesc,
id: Number(this.$route.params.id),
});
this.$router.push("/");
},
},
};
</script>
Try follwoing:
beforeRouteEnter(to, from, next) {
const task = this.$store.getters.getTaskById(Number(this.$route.params.id));
if (!task) this.$router.push("/404");
},
<script>
export default {
name: "EditTaskForm",
data() { ... },
mounted() { ... },
methods: { ... },
beforeRouteEnter(to, from, next) {
const task = this.$store.getters.getTaskById(Number(this.$route.params.id));
if (!task) this.$router.push("/404");
}
};
</script>
Navigation Guards

listen to events from dynamic vue components

How would you listen to an event emitted by a dynamically created component instance?
In the example, the top component is added in the DOM, while the second is dynamically created in javascript.
Vue.component("button-counter", {
data: function() {
return {
count: this.initial_count
}
},
props: ['initial_count'],
methods: {
add: function() {
this.count++
this.$emit('myevent', this.count)
}
},
template: '<button v-on:click="add">You clicked me {{ count }} times.</button>'
})
let app = new Vue({
el: "#app",
data() {
return {
initial_count: 10,
}
},
mounted: function() {
let initial_count = this.initial_count
let ButtonCounterComponentClass = Vue.extend({
data: function() {
return {}
},
render(h) {
return h("button-counter", {
props: {
initial_count: initial_count
}
})
}
})
let button_counter_instance = new ButtonCounterComponentClass()
button_counter_instance.$mount()
button_counter_instance.$on('myevent', function(count) {
console.log('listened!')
this.say(count)
})
this.$refs.container.appendChild(button_counter_instance.$el)
},
methods: {
say: function(message) {
alert(message)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<button-counter initial_count=20 v-on:myevent="say"></button-counter>
<div ref='container'></div>
</div>
If I've understood what you want then you just need to listen for the event on the inner component and pass it on.
I've used arrow functions in a couple of places to avoid problems with this bindings. Otherwise I've tried to leave your code unchanged as much as possible. Changes marked with ****.
Vue.component("button-counter", {
data: function() {
return {
count: this.initial_count
}
},
props: ['initial_count'],
methods: {
add: function() {
this.count++
this.$emit('myevent', this.count)
}
},
template: '<button v-on:click="add">You clicked me {{ count }} times.</button>'
})
let app = new Vue({
el: "#app",
data() {
return {
initial_count: 10,
}
},
mounted: function() {
let initial_count = this.initial_count
let ButtonCounterComponentClass = Vue.extend({
data: function() {
return {}
},
render(h) {
return h("button-counter", {
props: {
initial_count: initial_count
},
// **** Added this ****
on: {
myevent: count => {
this.$emit('myevent', count);
}
}
// ****
})
}
})
let button_counter_instance = new ButtonCounterComponentClass()
button_counter_instance.$mount()
// **** Changed the next line ****
button_counter_instance.$on('myevent', count => {
console.log('listened!')
this.say(count)
})
this.$refs.container.appendChild(button_counter_instance.$el)
},
methods: {
say: function(message) {
alert(message)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<button-counter initial_count=20 v-on:myevent="say"></button-counter>
<div ref='container'></div>
</div>
It's important to understand that button_counter_instance is not an instance of your button-counter component. You've wrapped it in another component, albeit a component that doesn't add any extra DOM nodes. So listening on the wrapper component is not the same as listening on button-counter.
Docs for what you can pass to h: https://v2.vuejs.org/v2/guide/render-function.html#The-Data-Object-In-Depth

How to destroy vuejs component from outside of component

I have created one of component in vuejs something like this
var tree_data = Vue.extend({
template: '#tree_data_template',
props: [
'groupmodal',
'search-name',
'tree-id'
], // props in single quotes
data: function () {
return {
shared: d2d.store
};
}
});
And use this component in another template like this.
var template_data = Vue.extend({
template: '#template_data',
created: function () {
var self = this;
self.shared.setCurrentRoute('templates');
},
components: {
'tree-data': tree_data
},
data: function () {
return {
id: this.$route.params.id,
shared: d2d.store,
};
},
methods: {
destroyComponent: function () {
//Need to code for destroy tree-data component
}
}
});
Blade file code
<tree-data
groupmodal="false"
search-name="user_search"
tree-id="user_tree"
>
</tree-data>
So finally how can i destroy my "tree-data" component through the "destroyComponent()" method
As cobaltway said you can use v-if
Setting v-if initially to false will render(generate) the component.
Then in your method setting v-if to true will destroy the component.
html
<div id="template_data">
<tree-data v-if="destroyComponent"></tree-data>
</div>
script
var template_data = Vue.extend({
template: '#template_data',
created: function () {
var self = this;
self.shared.setCurrentRoute('templates');
},
components: {
'tree-data': tree_data
},
data: function () {
return {
id: this.$route.params.id,
shared: d2d.store,
destroyComponent:true
};
},
methods: {
destroyComponent: function () {
//Need to code for destroy tree-data component
this.destroyComponent = false;
}
}
});
Here is the fiddle

VueJS: Setting data initially based on http response

So I have a template .vue file:
<template>
<div id="app">
<textarea v-model="input" :value="input" #input="update"></textarea>
<div v-html="compiledMarkdown"></div>
</div>
</template>
<script>
var markdown = require('markdown').markdown;
export default {
name: 'app',
data() {
return {
input: '# Some default data'
}
},
mounted: function () {
this.$nextTick(function () {
this.$http.get(window.location.pathname + '/data').then((response) => {
this.input = response.body.markdown;
}) })
},
computed: {
compiledMarkdown: function() {
this.$http.post(window.location.pathname, {
"html": markdown.toHTML(this.input)}).then(function() {
},function() {
});
return markdown.toHTML(this.input);
}
},
methods: {
update: function(e) {
this.input = e.target.value
}
}
}
</script>
In the mounted function I am trying to set input equal to the response of an HTTP request, but when you view this file this.input is still the same as it was initially declared. How can I change this.input inside the compiledMarkdown function to be this.input in the mounted function. What other approaches might I take?
You can not call a async method from a computed property, you can use method or watcher to run asynchronous code, from docs
This is most useful when you want to perform asynchronous or expensive operations in response to changing data.
You have to ran that relevant code when input changes, like following:
var app = new Vue({
el: '#app',
data: {
input: '# Some default data',
markdown : ''
},
methods: {
fetchSchoolData: function (schoolId) {
var url = this.buildApiUrl('/api/school-detail?schoolId=' + schoolId);
this.$http.get(url).then(response => {
this.schoolsListData = response.data;
}).catch(function (error) {
console.log(error);
});
},
},
mounted: function () {
this.$nextTick(function () {
this.$http.get(window.location.pathname + '/data').then((response) => {
this.input = response.body.markdown;
})
})
},
watch: {
// whenever input changes, this function will run
input: function (newInput) {
this.$http.post(window.location.pathname, {
"html": markdown.toHTML(this.input)}).then(function() {
},function() {
this.markdown = markdown.toHTML(this.input);
});
}
},
Have a look at my similar answer here.

VueJS Extends Methods and Data

I would like to extend methods. Example:
Vue.extend({
data: function () {
return {
fetcData: 'Test',
}
},
methods: function(){
return {
modal: function(){ alert('Modal') },
}
}
});
Vue.extend({
....
});
I use multiple extends.
// VueJS Instance
new Vue({
el: 'html',
data: {
effect: true
},
methods: {
xhrGet: function () {
alert(this.fetcData); // Undefined
this.modal(); // Undefined
},
xhrPost: function (event) {
alert('xhrPost')
}
}
});
Error Code:
this.fetchData is undefined.
this.modal is not a function
Vue.extend returns a new Vue component definition that can be later on instantiated using new keyword, it doesn't change the global Vue object. So if you want to create a component hierarchy try:
var BaseComponent = Vue.extend({
methods: {
foo: function() { console.log('foo') }
}
})
var MyComponent = BaseComponent.extend({
methods: {
bar: function() {
this.foo()
console.log('bar')
}
}
})
let myComponentInstance = new MyComponent()
Check out that fiddle for a live example.