Component's values not being updated - javascript

I created a Vue app, where I have a single HTTP request. Once the data returns, I update the profile value. But all components using this value do not get reloaded. Below is the code to better explain what I am trying to accomplish.
I have the main Vue file App.vue:
<template>
<div id="app">
<Navigation />
<Header :profile="profile" />
<About :profile="profile" />
<Services :profile="profile" />
</div>
</template>
<script>
import Navigation from './components/Navigation.vue'
import Header from './components/Header.vue'
import About from './components/About.vue'
import Services from './components/Services.vue'
export default {
name: 'app',
components: {
Navigation,
Header,
About,
Services,
},
data() {
return {
profile: { }
}
},
created() {
this.getUserProfile()
},
methods: {
getUserProfile: async function() {
try {
const response = await fetch('http://localhost:7070/v1/home');
const data = await response.json();
this.profile = data;
} catch (error) {
console.error(error);
}
}
}
}
</script>
<style>
</style>
As you can see, I set the variable profile to empty object at the start. And once the app enters the mounted state, I can the GET request to retrieve the profile data. When I debug, I can clearly see data is not an empty object. The response contains all data.
As you can see from the app file, I import 4 files to add 4 components to the app. All components are done in the same principle.
Here is the navigation.vue content:
<template>
<nav class="navbar navbar-expand-lg navbar-light fixed-top py-3" id="mainNav">
<div class="container">
<a class="navbar-brand js-scroll-trigger" href="#page-top">Home</a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto my-2 my-lg-0">
<li class="nav-item">
<a class="nav-link js-scroll-trigger" href="#about">About</a>
</li>
<li class="nav-item">
<a class="nav-link js-scroll-trigger" href="#services">Services</a>
</li>
<li class="nav-item">
<a class="nav-link js-scroll-trigger" href="#career">Career</a>
</li>
<li class="nav-item">
<a class="nav-link js-scroll-trigger" href="#contact">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
</template>
<script>
</script>
<style scoped>
</style>
Component header.vue:
<template>
<header class="masthead">
<div class="container h-100">
<div class="row h-100 align-items-center justify-content-center text-center">
<div class="col-lg-10 align-self-end">
<h1 class="text-uppercase text-white font-weight-bold">{{ fullName }}</h1>
<h2 class="text-uppercase text-white font-weight-bold">{{ profession }}</h2>
<hr class="divider my-4">
</div>
<div class="col-lg-8 align-self-baseline">
<p class="text-white-75 font-weight-light mb-5">{{ oneLiner }}</p>
<a class="btn btn-primary btn-xl js-scroll-trigger" href="#about">Find Out More</a>
</div>
</div>
</div>
</header>
</template>
<script>
export default {
props: {
profile: Object
},
computed: {
fullName: function () {
if (typeof(profile) == 'undefined') {
return 'Jernej Klancic';
}
return profile.firstname + ' ' + profile.lastname;
},
profession: function() {
if (typeof(profile) == 'undefined') {
return 'Developer';
}
return profile.profession;
},
oneLiner: function() {
if (typeof(profile) == 'undefined') {
return '';
}
return profile.oneLiner;
}
}
}
</script>
<style scoped>
</style>
Component about.vue:
<template>
<section class="page-section bg-primary" id="about">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8 text-center">
<h2 class="text-white mt-0">About</h2>
<hr class="divider light my-4">
<p class="text-white-50 mb-4">{{ aboutMe }}</p>
<a class="btn btn-light btn-xl js-scroll-trigger" href="#services">Expertise</a>
</div>
</div>
</div>
</section>
</template>
<script>
export default {
props: {
profile: Object
},
computed: {
aboutMe: function () {
if (typeof(profile) == 'undefined') {
return '';
}
return profile.aboutMe;
}
}
}
</script>
<style scoped>
</style>
Component service.vue:
<template>
<section class="page-section" id="services">
<div class="container">
<h2 class="text-center mt-0">At Your Service</h2>
<hr class="divider my-4">
<div class="row">
<div v-for="skill in skills" v-bind:key="uuid" class="col-lg-3 col-md-6 text-center">
<div class="mt-5">
<i v-bind:class="fontAwesomeDecorator(skill)"></i>
<h3 class="h4 mb-2">{{ skill.type }}</h3>
<p class="text-muted mb-0">{{ skillLevel(skill) }}</p>
</div>
</div>
<div class="col-lg-3 col-md-6 text-center">
<div class="mt-5">
<i class="fas fa-4x fa-laptop-code text-primary mb-4"></i>
<h3 class="h4 mb-2">Other</h3>
<p class="text-muted mb-0">Always eager to learn new language or framework</p>
</div>
</div>
</div>
</div>
</section>
</template>
<script>
export default {
props: {
profile: Object
},
computed: {
skills: function () {
if (typeof(profile) == 'undefined') {
return [];
}
return profile.favoriteExpertise.proficient.slice(0, 3);
}
},
methods: {
fontAwesomeDecorator: function(skill) {
var style = ['fab', 'fa-4x', 'text-primary', 'mb-4'];
var uppercase = skill.type.toUpperCase();
if (uppercase === 'JAVA') {
style.push('fa-java');
}
if(uppercase === 'JAVASCRIPT') {
style.push('fa-js');
}
if(uppercase === 'ANDROID') {
style.push('fa-android');
}
return style;
},
skillLevel: function(skill) {
switch(skill.rating) {
case 10:
return `Living and breathing ${skill.type} code`;
case 9:
return `${skill.type} marksmen`;
case 8:
return `Bug slayer in the ${skill.type} realm`;
case 7:
return `${skill.type} fanboy`;
case 6:
return `Level ${skill.rating} ${skill.type} wizard`;
case 5:
return `${skill.type} nerd`;
default:
return `${skill.type} motivator stage ${skill.type}`;
}
}
}
}
</script>
<style scoped>
</style>
Can anyone tell what could be wrong? Should I change my approach? Is there a better way to do this in Vue? As said, I retrieve the profile JSON object from the backend. I reassign the profile variable by using this.profile = data; in App.vue. Should this not trigger a reload of data?

You need to access the profile prop using this.profile within the JavaScript portion of you component.
For example, this won't work:
skills: function () {
if (typeof(profile) == 'undefined') {
return [];
}
return profile.favoriteExpertise.proficient.slice(0, 3);
}
Instead of just profile you need to write this.profile.
The ability to drop the this. in the template does not carry over to elsewhere. That's specifically a feature of the templating language. In the <script> section you need to include the this. just like you would in any other JavaScript code. Props, data, computed properties and methods are all accessed as properties of the Vue instance in this way.

Related

How to pass Books array length to the parent component from child component in vue.js?

I am developing one page which is responsible from displaying books and the response is coming from backend API, DisplayBooks.vue component is imported inside a Dashboard.vue (parent-component).inside DisplayBooks.vue it contains books [] array , i want to pass this length to the Dashboard.vue component ,How to acheive this thing please help me to fix this thing..
Dashboard.vue
<template>
<div class="main">
<div class="navbar navbar-default navbar-fixed-top">
<div class="navbar-header">
<img src="../assets/education.png" alt="notFound" class="education-image" />
</div>
<ul class="nav navbar-nav">
<li>
<p class="brand">Bookstore</p>
</li>
</ul>
<div class="input-group">
<i #click="handlesubmit();" class="fas fa-search"></i>
<div class="form-outline">
<input type="search" v-model="name" class="form-control" placeholder='search...' />
</div>
</div>
<ul class="nav navbar-nav navbar-right" id="right-bar">
<li><a> <i class="far fa-user"></i></a></li>
<p class="profile-content">profile</p>
<li #click="shownComponent='Cart'"><a><i class="fas fa-cart-plus"></i></a></li>
<p class="cart-content" >cart <span class="length" v-if="booksCount">{{booksCount}}</span></p>
</ul>
</div>
<div class="mid-body">
<!-- i want to display books length here -->
<h6>Books<span class="items">({{books.length}})</span></h6>
<select class="options" #change="applyOption">
<option disabled value="">Sort by relevance</option>
<option value="HighToLow">price:High to Low</option>
<option value="LowToHigh">price:Low to High</option>
</select>
</div>
<div v-if="flam==false">
<h2>Hello</h2>
</div>
<DisplayBooks v-show="flag==='noOrder' && shownComponent==='DisplayBooks'" #update-books-count="(n)=>booksCount=n" />
<Cart v-show=" shownComponent==='Cart'" />
<sortBooksLowtoHigh v-show="flag==='lowToHigh'" #update-books-count="(n)=>booksCount=n" />
<sortBooksHightoLow v-show="flag==='highToLow'" #update-books-count="(n)=>booksCount=n" />
</div>
</template>
<script>
import sortBooksLowtoHigh from './sortBooksLowtoHigh.vue'
import sortBooksHightoLow from './sortBooksHightoLow.vue'
import DisplayBooks from './DisplayBooks.vue'
import Cart from './Cart.vue'
export default {
components: {
DisplayBooks,
sortBooksLowtoHigh,
sortBooksHightoLow,
Cart
},
data() {
return {
shownComponent:'DisplayBooks',
booksCount:0,
flag: 'noOrder',
brand: 'Bookstore',
name: '',
flam: true,
visible: true,
}
},
methods: {
flip() {
this.flam = !this.flam;
},
applyOption(evt) {
if (evt.target.value === "HighToLow") {
this.flag = 'highToLow';
} else this.flag = 'lowToHigh';
},
}
}
</script>
DisplayBooks.vue
<template>
<div class="carddisplay-section">
<!-- <div class="mid">
<h6>Books<span class="items">({{books.length}})</span></h6>
</div> -->
<div v-for="book in books" :key="book.id" class="card book" >
<div class="image-section" #mouseover="book.hover = true" #mouseleave="book.hover = false">
<div class="image-container">
<img v-bind:src="book.file" />
</div>
</div>
<div class="title-section" >
{{book.name}}
</div>
<div class="author-section">
by {{book.author}}
</div>
<div class="price-section">
Rs. {{book.price}}<label class="default">(2000)</label>
</div>
<div class="buttons">
<div class="button-groups" v-if="!addedBooks.includes(book.id)">
<button type="submit" #click="handleCart(book.id);toggle(book.id);addedBooks.push(book.id)" class="AddBag">Add to Bag</button>
<button class="wishlist">wishlist</button>
</div>
<div class="AddedBag" v-else>
<button class="big-btn" #click="removeFromCart(book.id);addedBooks=addedBooks.filter(id=>id!==book.id)">Added to Bag</button>
</div>
</div>
<div class="description" v-if="book.hover">
<p class="hovered-heading">BookDetails</p>
{{book.description}}
</div>
</div>
</div>
</template>
<script>
import service from '../service/User'
export default {
mounted() {
service.userDisplayBooks().then(response => {
let data = response.data;
data.map(function(obj) {
obj.hover = false;
return obj;
});
this.books.push(...data);
return response;
})
},
data() {
return {
isActive:true,
result: 0,
authorPrefix: 'by',
pricePrefix: 'Rs.',
defaultStrikePrice: '(2000)',
buttonValue: 'Add to Bag',
buttonWishlist:'wishlist',
buttonAddedBag:'Added to Bag',
flag: true,
state: true,
addedBooks:[],
clickedCard: '',
books: [] // i want to display this array length
}
},
watch:{
addedBooks:{
handler(val){
this.$emit('update-books-count',val.length)
},
deep:true
}
},
methods: {
toggleClass: function(event){
this.isActive = !this.isActive;
return event;
},
toggle(id) {
this.clickedCard = id;
console.log(this.clickedCard);
},
flip() {
this.state = !this.state;
},
Togglebtn() {
this.flag = !this.flag;
},
handleCart(bookId){
let userData={
id: bookId,
}
service.userUpdateCart(userData).then(response=>{
return response;
}).catch(error=>{
alert("error while displaying Books");
return error;
})
},
removeFromCart(bookId){
let userData={
id:bookId,
}
service.userRemoveFromCart(userData).then(response=>{
return response;
}).catch(error=>{
alert("error while removing from cart");
return error;
})
}
}
}
</script>

angular 8 - Error trying to diff '[object Object]'. Only arrays and iterables are allowed

I want to display search results, but whenever i am trying to type anything to search box it shows error in console. i am fetching data from api and according to type i am displaying results in tabs.
data is getting filtered according to nav-tabs type.
but now i want to search listed data,
so whenever i am trying to search is shows error
TS -
searchKeywords: string;
CoffeeItemList: any = [];
// tslint:disable-next-line:max-line-length
constructor(private getDataListingService: DataListingService){}
ngOnInit() {
this.getGlobalSearchList('');
}
getGlobalSearchList(type: string) {
this.CoffeeItemList = [];
this.getDataListingService.getAllDataLists().subscribe(value => {
let data = [];
data = value.data;
console.log(data);
for (let i = 0; i < data.length - 1; i++) {
if (data[i].type === type) {
this.CoffeeItemList.push(data[i]);
}
}
});
}
getSmartSearchValues(search: string) {
if (search === '' ) {
this.getGlobalSearchList('');
return false;
}
this.getDataListingService.searchList(search).subscribe((data: any) => {
this.CoffeeItemList = data;
});
HTML
<div class="container">
<div class="mt-4">
<input class="form-control" type="text" [(ngModel)]="searchKeywords" (keyup)="getSmartSearchValues(searchKeywords)" placeholder="Search here"/>
</div>
<br>
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a class="nav-link active" data-toggle="tab" href="#list" (click)="getGlobalSearchList('DancingGoatMvc-Coffee')">Coffee</a>
</li>
<li class="nav-item">
<a class="nav-link" data-toggle="tab" href="#list" (click)="getGlobalSearchList('DancingGoatMvc-Brewer')">Brewer</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div id="list" class="tab-pane container active in"><br>
<div class="row">
<div class="card col-3" *ngFor="let items of CoffeeItemList">
<div class="card-body">
<h5 class="card-title">{{items?.title }}</h5>
<img src="http://infogainpune.com{{items.image |slice:1}}" class="w-100"/>
<p class="card-text">{{items?.content}}</p>
<h4 class="card-text">${{items?.price}}</h4>
<h4 class="card-text">{{items?.type}}</h4>
</div>
</div>
</div>
</div>
</div>
</div>
The response of data
Try to change your code like this too see if it work for you
this.getDataListingService.getAllDataLists().subscribe(value => {
this.CoffeeItemList = value.data;
});
Let me know if you still have a problem.

Vue elements not being displayed

I am trying to create Vue elements using template elements in the body of the HTML. It was working pretty well until I tried to use two of them together.
<template id="root-template">
<div>
<app-navbar targetId="#app-sidenav" />
<app-header />
</div>
</template>
For some reason, commenting out app-navbar causes app-header to be displayed, however if app-navbar is displayed, app-header is not being displayed at all! This one has completely stumped me.
const Navbar = Vue.component("app-navbar", {
name: "app-navbar",
template: "#navbar-template",
props: {
targetId: {
type: String,
default: "#app-sidenav"
}
},
mounted() {
this.sidenav = M.Sidenav.init(document.querySelector(this.targetId));
},
});
const Header = Vue.component("app-header", {
name: "app-header",
template: "#header-template"
});
const AppRoot = Vue.component("app-root", {
template: "#root-template",
components: {
Navbar,
Header
},
});
const store = new Vuex.Store({
state: {},
getters: {},
mutations: {},
actions: {}
});
const app = new Vue({
el: "#app",
store,
render: h => h(AppRoot)
});
<script src="https://unpkg.com/vuex#3.1.2/dist/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons&type=.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<ul class="sidenav" id="app-sidenav">
<li>Sass</li>
<li>Components</li>
<li>Javascript</li>
<li>Mobile</li>
</ul>
<div id="app" />
<template id="navbar-template">
<nav>
<div class="nav-wrapper container">
Logo
<a href="#" data-target="app-sidenav" class="sidenav-trigger">
<i class="material-icons">menu</i>
</a>
<ul class="right hide-on-med-and-down">
<li>Sass</li>
<li>Components</li>
<li>Javascript</li>
<li>Mobile</li>
</ul>
</div>
</nav>
</template>
<template id="header-template">
<div class="section no-pad-bot" id="index-banner">
<div class="container">
<br /><br />
<h1 class="header center orange-text">Todos</h1>
<div class="row center">
<h5 class="header col s12 light">
A modern responsive front-end framework based on Material Design
</h5>
</div>
<div class="row center">
<a href="http://materializecss.com/getting-started.html" id="download-button"
class="btn-large waves-effect waves-light orange">Get Started</a>
</div>
<br /><br />
</div>
</div>
</template>
<template id="root-template">
<div>
<app-navbar target-id="#app-sidenav" />
<app-header />
</div>
</template>
I've also turned this into a Pen. Can anyone tell me why this is happening?
Don't use self-closing tags in regular HTML files.
If you change these lines of HTML:
<app-navbar target-id="#app-sidenav" />
<app-header />
to these:
<app-navbar target-id="#app-sidenav"></app-navbar>
<app-header></app-header>
it will work.
You should put the templates in the functions if you're going with a single file approach. See the below code for an example. The templates aren't parsing properly, because of the self closing syntax, and hence only displaying the one component. When you use template strings you can get the expected display from the templates how you have them now. Just put them inside the component function's template: like so:
const Navbar = Vue.component("app-navbar", {
name: "app-navbar",
template:
`
<nav>
<div class="nav-wrapper container">
Logo
<a href="#" data-target="app-sidenav" class="sidenav-trigger">
<i class="material-icons">menu</i>
</a>
<ul class="right hide-on-med-and-down">
<li>Sass</li>
<li>Components</li>
<li>Javascript</li>
<li>Mobile</li>
</ul>
</div>
</nav>
`,
props: {
targetId: {
type: String,
default: "#app-sidenav"
}
},
mounted() {
this.sidenav = M.Sidenav.init(document.querySelector(this.targetId));
},
});
const Header = Vue.component("app-header", {
name: "app-header",
template:
`
<div class="section no-pad-bot" id="index-banner">
<div class="container">
<br /><br />
<h1 class="header center orange-text">Todos</h1>
<div class="row center">
<h5 class="header col s12 light">
A modern responsive front-end framework based on Material Design
</h5>
</div>
<div class="row center">
<a href="http://materializecss.com/getting-started.html" id="download-button"
class="btn-large waves-effect waves-light orange">Get Started</a>
</div>
<br /><br />
</div>
</div>
`
});
const AppRoot = Vue.component("app-root", {
template:
`
<div>
<!-- Comment out the navbar to see the header. expected behavior is that both ar displayed -->
<app-navbar target-id="#app-sidenav" />
<app-header />
</div>
`,
components: { Navbar, Header },
});
store = new Vuex.Store({
state: {},
getters: {},
mutations: {},
actions: {}
});
app = new Vue({
el: "#app",
store,
render: h => h(AppRoot)
});
Remove the templates in your code pen and add this to the JS to see it working. Alternatively you can use the closing tags syntax for your components, but it's more readable and intuitive to use self closing tags and separate your display bits into their functions instead of a large blob of <template></template> tags. Just my opinion of cleaner code, so feel free to use either approach as desired/needed.

Axios making DELETE and GET in the wrong order

I'm working on a small project in vue.js connected to a lumen API (working).
I currently have a list of students ('Etudiants') in which I can click in the list to select one, and delete it via a button in a toolbar.
When a student is deleted I'm reloading the student list (since it's not up to date anymore), therefore I'm doing 2 api calls via axios.
DELETE http://www.url.com/etudiants (param: idEtudiant)
GET http://www.url.com/etudiants (param: page)
The problem is that my API calls are not done in the right order, as shown here on a screenshot of the calls (with watterfall):
This problem involves 3 vue files.
'Etudiants.vue' and its 2 childs: 'ListeEtudiants.vue' (student list) and 'BarreOutilsEtudiant.vue' (toolbar)
This simple image shows the hierarchy of the 3 files and the order of which everything should execute.
In my case (when it's not working) the action number 3, the axios DELETE, happens in last.
Here are the contents of my files:
Etudiants.vue:
<template>
<div id="etudiants" class="container-fluid h-100">
<div class="row">
<div class="col-3 borderR">
<ListeEtudiants ref="list" #idEtudiantChanged="updateIdEtudiant"/>
</div>
<div class="col-9 bg-light">
<BarreOutilsEtudiant v-if="idEtudiant != null" :idEtudiant="idEtudiant" #delEtudiant="delEtudiant"/>
<InfosEtudiant v-if="idEtudiant != null" :idEtudiant="idEtudiant"/>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import ListeEtudiants from '#/components/ListeEtudiants.vue'
import InfosEtudiant from '#/components/InfosEtudiant.vue'
import BarreOutilsEtudiant from '#/components/BarreOutilsEtudiant.vue'
export default {
components: {
ListeEtudiants,
InfosEtudiant,
BarreOutilsEtudiant
},
data: function(){
return {
idEtudiant: null
}
},
methods:{
updateIdEtudiant(idEtudiant){
this.idEtudiant=idEtudiant;
},
delEtudiant(){
axios
.delete('http://82ab2617.ngrok.io/etudiants', {params: {"idEtudiant" :this.idEtudiant}})
.then(this.$refs.list.loadList())
.catch(error => console.log(error));
}
}
}
</script>
ListeEtudiants.vue:
<template>
<div id="ListeEtudiants">
<div class="row bg-light">
<!-- Trigger Modal Ajout Etudiant -->
<button class="btn btn-light w-100" data-toggle="modal" data-target="#addModal">
<font-awesome-icon icon="plus" size="1x"/>
<span> Ajouter un Etudiant</span>
</button>
</div>
<ul v-if="etudiants != null" id="list" class="row flex-nowrap list-group list-group-flush pr-0">
<button v-for="etudiant in etudiants.data" v-on:click='$emit("idEtudiantChanged",etudiant.idEtudiant)' class="btn btn-light text-left list-group-item pl-5 py-1">{{ etudiant.nom }} {{ etudiant.prenom }}</button>
</ul>
<ul v-else class="row flex-nowrap list-group list-group-flush pr-0">
</ul>
<div class="row bg-light">
<button class="btn btn-light col-3" v-on:click="page -= 1" :disabled="page === 1 || disabled"><font-awesome-icon icon="chevron-left" size="1x"/></button>
<div class="align-middle col-6 my-auto">{{ page }} / {{ maxPage }}</div>
<button class="btn btn-light col-3" v-on:click="page += 1" :disabled="page === maxPage || disabled"><font-awesome-icon icon="chevron-right" size="1x"/></button>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: "ListeEtudiants",
data: function(){
return {
etudiants: null,
maxPage:1,
disabled:false,
page:1,
}
},
methods: {
parseAndDisplay: function(data){
this.etudiants = data;
this.maxPage = data.last_page;
this.page = data.current_page;
this.disabled = false;
},
loadList: function(){
this.disabled = true;
this.etudiants = null;
axios
.get('http://82ab2617.ngrok.io/etudiants', {params: {page:this.page}})
.then(response =>this.parseAndDisplay(response.data))
.catch(error => console.log(error));
}
},
watch: {
'page': function(newVal, oldVal){
if((newVal === 0 && oldVal === 1) || (newVal === this.maxPage+1 && oldVal === this.maxPage)){
this.page = oldVal;
}else{
if(oldVal !== 0 && oldVal !== this.maxPage+1) {
this.loadList();
}
}
}
}
,
mounted() {
this.loadList();
}
}
</script>
BarreOutilsEtudiant.vue:
<template>
<div class="row p-2 navbar-expand navbar-info bg-info">
<button class="btn btn-info mr-5" type="button"><font-awesome-icon icon="download" size="1x"/> Télécharger le Bulletin</button>
<button class="btn btn-info ml-auto" type="button"><font-awesome-icon icon="user-edit" size="1x"/></button>
<button class="btn btn-danger ml-4" v-on:click="$emit('delEtudiant')" type="button"><font-awesome-icon icon="trash-alt" size="1x"/></button>
</div>
</template>
<script>
export default {
name: "BarreOutilsEtudiant"
}
</script>
<style scoped>
</style>
Thank you very much for helping me.
I believe the problem is here:
.then(this.$refs.list.loadList())
That will call loadList immediately and pass the value it returns to then, which isn't what you want.
Instead it should be something like this, wrapping it in a function:
.then(() => this.$refs.list.loadList())

Vue JS dynamic modal with components

in news.twig
{% extends 'layouts.twig' %}
{% block content %}
<section class="ls section_padding_bottom_110">
<div id="cart" class="container">
<cart
v-bind:materials="news"
type="news"
test="materials"
></cart>
<modal></modal>
</div>
<script type="text/x-template" id="modal-template">
<transition name="modal">
<div class="modal-mask" v-if="active" #click="close()">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<h3>${ item.name }</h3>
</div>
<div class="modal-body">
${ item.body }
<br>
modal #${ item.id }
</div>
<div class="modal-footer">
<button class="modal-default-button" #click="close()">
close
</button>
</div>
</div>
</div>
</div>
</transition>
</script>
</section>
{% endblock %}
I have 2 components and 1 Vue in my js.
var Hub = new Vue();
Vue.component(
'modal', {
template: '#modal-template',
delimiters: ['${', '}'],
data: function() {
return {
active: false,
item: {
id: '',
name: '',
body: ''
}
}
},
methods: {
open: function (item) {
this.active = true;
this.item = item;
},
close: function () {
this.active = false;
}
},
mounted: function() {
this.$nextTick(function () {
Hub.$on('open-modal', this.open);
Hub.$on('close-modal', this.close);
}.bind(this));
}
});
Vue.component('cart', {
props: {
materials: { type: Array, required: true},
type: { type: String, required: true}
},
computed: {
isPoints() {
return this.type == 'paymentPoints';
},
isNews() {
return this.type == 'news';
}
},
template : `
<div class="row masonry-layout isotope_container">
<div class="col-md-4 col-sm-6 isotope-item" v-for="item in materials">
<div class="vertical-item content-padding topmargin_80">
<div class="item-media">
<img v-bind:src="item.image" alt="">
<div class="media-links p-link">
<div class="links-wrap">
<i class="flaticon-arrows-2"></i>
</div>
<a v-if="!isNews" v-bind:href="item.image" class="abs-link"></a>
</div>
</div>
<button #click="openModal(item)" #keyup.esc="closeModal()">more</button>
<div class="item-content with_shadow with_bottom_border">
<span v-if="isNews" class="categories-links" style="font-size:20px;">
<a rel="category" href="#modal1" data-toggle="modal">
{{item.name}}
</a>
</span>
<p>{{item.body}}</p>
<div v-if="isPoints">
<hr class="light-divider full-content-divider bottommargin_10">
<div class="media small-icon-media topmargin_5">
<div class="media-left">
<i class="fa fa-map-marker grey fontsize_18"></i>
</div>
<div class="media-body">
{{item.adress}}
</div>
</div>
<div class="media small-icon-media topmargin_5">
<div class="media-left">
<i class="fa fa-phone grey fontsize_18"></i>
</div>
<div class="media-body">
{{item.telephone}}
</div>
</div>
</div>
<div v-if="isNews" class="text-center">
<hr class="light-divider full-content-divider bottommargin_10">
<span class="date">
<i class="flaticon-clock-1 grey"></i>
<time class="entry-date">
{{item.date}}
</time>
</span>
</div>
</div>
</div>
</div>
</div>
`
});
var vm = new Vue({
el: '#cart',
name: 'cart',
delimiters: ['${', '}'],
data: {
complated: [],
continuing: [],
planned: [],
points: [],
infoSlider: [],
news: []
},
methods: {
openModal: function (item) {
Hub.$emit('open-modal', item);
},
closeModal: function () {
Hub.$emit('close-modal');
}
},
created() {
axios.get(url).then(res => {
var proje = res.data.projects[0];
this.complated = proje.complated;
this.continuing = proje.continuing;
this.planned = proje.planned;
this.points = res.data.paymentPoints;
this.infoSlider = res.data.sliderİnfos;
this.news = res.data.news;
})
.catch(e => {
console.log(e);
})
}
});
When I click openModal(item) button give me error ;
Property or method "openModal" 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. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I can not use the openModal function in component.
I can use the function in news.twig without any problems, but then I can not use the component. Can you help me?
You are using openModal in cart component, but that method is defined in root component.
According to Vue's documentation:
Everything in the parent template is compiled in parent scope;
everything in the child template is compiled in the child scope.
in my case need to define variable in vuejs
like this
<script>
export default {
name: "MegaMenu",
props: {
categories: Array,
},
}

Categories

Resources