Components inside components VueJS - javascript

I'm new to VueJs and I'm having trouble with the following (excuse me in advance if the question seems dumb but I haven't found an answer to this yet):
var data = {
info: [
{
brand: 'Samsung',
model: 'A9834',
color: 'black',
id: 0
},
{
brand: 'Nokia',
model: 'Z9234',
color: 'blue',
id: 2
}
]
}
Vue.component('list-group', {
template: '<ul class="list-group"><list-group-item v-for="item in info" v-bind:key="item.id" v-bind:properties="item"></list-group-item></ul>'
})
Vue.component('list-group-item', {
template: '<li class="list-group-item">{{properties.brand}}, {{properties.model}}, {{properties.color}}</li>',
props: ['properties']
})
var instance = new Vue({
el: '.app',
data: data
})
What I'm trying to do with the snippet above is render the component inside the component. The error I get in the console is the following:
[Vue warn]: Property or method "info" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
As you can see, what I expect to get is two li elements inside a ul element (2 elements because I'm looping data.info with a v-for, and data.info has two elements for simplicity); but instead i'm getting the error above. There is obviously something I'm missing here knowledge-wise but I don't know what it is. If you could help me figure it out, I would appreaciate it a lot.
Thanks in advance.

Your code works fine with props: ['info']
https://codepen.io/bsalex/project/editor/ApjgQd#
var data = {
info: [
{
brand: "Samsung",
model: "A9834",
color: "black",
id: 0
},
{
brand: "Nokia",
model: "Z9234",
color: "blue",
id: 2
}
]
};
Vue.component("list-group", {
props: ["info"],
template:
'<ul class="list-group"><list-group-item v-for="item in info" v-bind:key="item.id" v-bind:properties="item"></list-group-item></ul>'
});
Vue.component("list-group-item", {
template:
'<li class="list-group-item">{{properties.brand}}, {{properties.model}}, {{properties.color}}</li>',
props: ["properties"]
});
var instance = new Vue({
el: ".app",
template: '<list-group :info="info">',
data: data
});

Related

How can i use my array/object data in the method object to then ref to the spceified link VUEJS

How can i possibly use the "LINK" in the methods object?
I think you can see what im trying to do, but if you don't here it is
With a array/object in VUEJS im trying to get all the data from the object in the Data method to then send the link object as a ref to open a new tab with the already defined link
const Main = new Vue({
el: ".ui",
data: {
socials: [
{label: "Discord", icon: "fab fa-discord", link: "https://discord.gg/kdnt67j"},
{label: "Twitch", icon: "fa fa-twitch", link: "https://www.twitch.tv/brezedc"}
]
},
methods: {
openLink: function( event ) {
var vm = this;
window.location = vm.socials.link
}
}
})
I don't see any reason to use a method for this when you can just use a plain old anchor tag.
For example
new Vue({
el: ".ui",
data: {
socials: [
{label: "Discord", icon: "fab fa-discord", link: "https://discord.gg/kdnt67j"},
{label: "Twitch", icon: "fab fa-twitch", link: "https://www.twitch.tv/brezedc"}
]
}
})
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.0-2/css/all.min.css" integrity="sha256-46r060N2LrChLLb5zowXQ72/iKKNiw/lAmygmHExk/o=" crossorigin="anonymous" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<ul class="ui">
<li v-for="social in socials">
<a target="_blank" :href="social.link">
<i :class="social.icon"></i> {{ social.label }}
</a>
</li>
</ul>
you just need to iterate socials data you have defined and throw the link to new tab
export default {
data() {
return {
socials: [
{label: "Discord", icon: "fab fa-discord", link: "https://discord.gg/kdnt67j"},
{label: "Twitch", icon: "fa fa-twitch", link: "https://www.twitch.tv/brezedc"}
]
};
},
watch:{
},
computed:{
},
methods: {
openLink() {
this.socials.map(val => window.open(val.link));
}
},
mounted(){
}
};
You need to send a parameter when using the openLink function besides the event parameter because otherwise you won't know which link to open. This parameter could be the actual URL (which would be a little bit redundant) or the index of the object in the array that contains the link.
So the first option would look something like this:
methods: {
openLink: function( event, link ) {
var vm = this;
window.location = link
}
}
and the second one something like this:
methods: {
openLink: function( event, index ) {
var vm = this;
window.location = vm.socials[index].link
}
}

Vue render button

My question is about render a button on vue instance, to click in a button and then it render another button with event click, If I simple mount the button it dont get the function tes.
const Hello = {
props: ['text'],
template: '<button v-on:click="tes"> </button> ',
};
new Vue({
el: '#app',
data: {
message: 'Click me'
},
methods:{
alertar: function(event){
const HelloCtor = Vue.extend(Hello);
var instance = new HelloCtor({
propsData: {
text: 'HI :)'
}
})
instance.$mount() // pass nothing
this.appendChild(instance.$el)
},
tes: function(){
alert('Teste');
}
}
})
Erro :
vue.js:597 [Vue warn]: Invalid handler for event "click": got undefined
(found in <Root>)
warn # vue.js:597
(index):52 Uncaught TypeError: this.appendChild is not a function
at Vue.alertar ((index):52)
at invoker (vue.js:2029)
at HTMLParagraphElement.fn._withTask.fn._withTas
The problem is that you create a child component inside of your parent Vue that contains the template with the binding to the tes function. That means that the child will look in its own methods for tes, however it is a property of your parent, not of the child itself so it will never be able to find it in its own scope. You have to add the function to the child component instead:
const Hello = {
props: ['text'],
template: '<button v-on:click="tes"> </button> ',
methods: {
tes: function(){
alert('Teste');
}
}
};
Just expanding #Philip answer
Basically you can't access parent methods in programatically created components.
You need to specify the methods inside the child components.
const Hello = {
props: ['text'],
template: '<button v-on:click="this.tes"> Vue Generated</button> ',
methods: {
tes: function(){
alert('Teste');
}}
};
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
mounted(){
this.alertar()
},
methods:{
alertar: function(event){
const HelloCtor = Vue.extend(Hello);
var instance = new HelloCtor({
propsData: {
text: 'HI :)'
}
})
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)
},
tes: function(){
alert('Teste');
}
}
})
Check this fiddle here
https://jsfiddle.net/50wL7mdz/370645/
However in some cases you may be able to access the parent components methods using
$parent directive which I believe will not work when components is created programatically.

The dynamically created components in vue.js shows random behaviour

What i want is, there a list made from json data. When i click on a item, it creates a new list dynamically.
Now when i click a different item in the first list, i want the second list to change depending on data i receive.
html structure is :
div class="subject-list container-list" id="app-1">
<item-subject
v-for="item in subjectlist"
v-bind:item="item"
v-bind:key="item.id"
>
</item-subject>
</div>
//some other code
<div class="exam-list container-list" id="app-2">
<item-exam
v-for="item in examlist"
v-bind:item="item"
v-bind:key="item.id"
>
</item-exam>
</div>
The main.js file is :
//Dummy json data
var subjects_json = { 'subjectlist': [
{ id: 0, text: 'Computer Graphics' },
{ id: 1, text: 'Automata Theory' },
{ id: 2, text: 'Programming in C' }
]};
var exams_json = { 'examlist': [
{ id: 0, text: 'IAT 1' },
{ id: 1, text: 'IAT 2' },
{ id: 2, text: 'Sem 2' }
]};
/*Contains definition of component item-subject...
Its method() contains call to exam component because it will be
called depending on the subject selected dynamically*/
Vue.component('item-subject', {
props: ['item'],
template: '<li v-on:click="showExams" class="subject-list-item">{{
item.text }}</li>',
methods: {
showExams: function(){
// alert(this.item.text)
console.log("Subject Clicked: "+this.item.text)
var app2 = new Vue({
el: '#app-2',
data: exams_json,
methods: {
showStudents: function(){
console.log("exams rendered")
}
}
})
},
}
});
//Contains definition of component item-exam.
Vue.component('item-exam', {
props: ['item'],
template: '<li v-on:click="showStudents" class="exam-list-item">{{ item.text }}</li>',
methods: {
showStudents: function(){
alert(this.item.text)
console.log("exam component executed")
// console.log("Exam Clicked: "+this.item)
}
}
});
//Call to subject component
var app1 = new Vue({
el: '#app-1',
data: subjects_json,
methods: {
showExams: function(){
console.log("subjects rendered")
}
}
})
So what this code does is, when i click on the first list i.e. subjects list, it dynamically renders new exams list.
Now when i click on second list, alert() method is called successfully.
However if i click any of the subject list(first list), now the alert() is not triggered while clicking second list.
Please tell me whats wrong.

Rendering a component directive in Vue 2 without params

I have an app that holds the student list data. The component is supposed to take in that list and render a select dropdown (with select2).
In the fiddles console, it's displaying jQuery is not defined. I thought all fiddles now included jQuery?
I'm really not sure why this is breaking the all together. Is there something wrong with my directive? I know with Vue 2.0 they removed params, but this should suffice. Any eyes on my code would be greatly appreciated.
// Define component
var studentsComponent = Vue.extend({
props: ['students'],
data(): {
return {}
},
methods:{},
directives: {
select: {
bind: function () {
var self = this;
var select = $('#select-student');
select.select2();
select.on('change', function () {
console.log('hey on select works!');
});
},
update: function (oldVal, newVal) {
var select = $('#select-student');
select.val(newVal).trigger('change');
}
},
},
template: `
<div>
<select
ref=""
id="select-student"
v-select>
<option value="0">Select Student</option>
<option
v-for="(student, index) in students"
:value="student.id">
{{ student.name }}
</option>
</select>
</div>
`,
});
// Register component
Vue.component('students-component', studentsComponent);
// App
new Vue({
el: '#app',
data: {
students: [
{ name: 'Jack', id: 0 },
{ name: 'Kate', id: 1 },
{ name: 'Sawyer', id: 2 },
{ name: 'John', id: 3 },
{ name: 'Desmond', id: 4 },
]
},
});
I made a fiddle https://jsfiddle.net/hts8nrjd/4/ for reference. Thank you for helping a noob out!
First, as I mentioned in comments, I would suggest you do this with a component. If you had to stick with a directive, however, you can't initialize select2 in the bind hook. You've defined your options in the DOM, so you need to wait until the component is inserted to initialize it.
directives: {
select: {
inserted: function (el, binding, vnode) {
var select = $(el);
select.select2();
select.on('change', function () {
console.log('hey on select works!');
});
},
},
},
Here is an update of your fiddle.

Marionette nested LayoutView - parsing model

I have Marionette/Backbone appliaction which is working fine. I wanted to add extra layer in our views:
Before:
TabLayoutView -> CompositeView
After:
TabLayoutView -> SectionLayoutView -> CompositeView
But this is not working and I can't see where is the problem.
Here is the code:
Model of tab:
TabModel = Backbone.Model.extend({
defaults: {
headerModel: {label: '', left: '', right: ''}
}
})
Template of tab:
<div class="headerSection"></div>
View of tab:
var TabLayoutView = Marionette.LayoutView.extend({
template: _.template(TabTemplate),
tagName: 'div',
regions: {
headerRegion: {selector: '.headerSection'}
},
onShow: function() {
this.headerRegion.show(new SectionLayoutView({model: this.model.get('headerModel')}));
}
});
Model of section:
SectionModel = Backbone.Model.extend({
defaults: {
label: '',
left: '',
right: ''
}
});
Template of section:
<div class="section">
<div class="leftSection"/>
<div class="rightSection"/>
</div>
View of section:
SectionLayoutView = Marionette.LayoutView.extend({
template: _.template(SectionTemplate),
tagName: 'div',
regions: {
leftRegion: {selector: '.section .leftSection'},
rightRegion: {selector: '.section .rightSection'}
},
onShow: function() {
this.leftRegion.show(new CompositeView(this.model.get('left')));
this.rightRegion.show(new CompositeView(this.model.get('right')));
}
});
Error I get is :
Uncaught TypeError: Cannot read property 'apply' of undefined
in the method
serializeModel: function(model) {
return model.toJSON.apply(model, _.rest(arguments));
}
which is triggered in this line:
this.headerRegion.show(new SectionLayoutView({model: this.model.get('headerModel')}));
Could you please give me any ideas of what is wrong? We have similar code in other places and it is working fine. It seems like there is a problem with parsing model to json, but I can't see why.
Because are you passing a plain Object to the view...
this.headerRegion.show(new SectionLayoutView({
model: this.model.get('headerModel') // NOT a Backbone.Model
});
Try this:
this.headerRegion.show(new SectionLayoutView({
model: new Backbone.Model(this.model.get('headerModel'))
});

Categories

Resources