Dynamic style binding in vue js to toggle a class - javascript

I am trying to bind an attribute to and id I have on a button to change its styling, basically I have a courses database table(using Laravel as backend) where each course has a Boolean named completed, All I want to do is if the course is completed(true) to render a specific id, and if it is not(false) to render another one, that's it, here's my code,
This is the blade template, this is inside a table:
<td>
<form method="POST" action="{{ route('course.completed', $course->name) }}" id="form-submit">
{{ csrf_field() }}
#if ($course->completed)
<button #click.prevent="onSubmit({{ $course }})" type="button" class="btn btn-sm" :id="cssClass">#{{ text }}</button>
#endif
</form>
And here is the vue instance, All i want to do here is to add an if condition that will set the cssClass properly to the name of the id that I want:
<script>
new Vue({
el: '#app',
data: {
cssClass: '',
text: ''
},
methods: {
onSubmit: function(course) {
axios.post('/MyCourses/' + course.name)
// .then(function (response){
// });
}
},
//Basically here's what I would like to be able to do
if (course.completed == true) {
this.cssClass = 'coursetogglingtrue',
this.text = 'Done!'
} else {
this.cssClass = 'coursetogglingfalse',
this.text = 'Not Yet!'
}
});
</script>
Right now the above code in the view instance errors out with "Uncaught SyntaxError: Unexpected token ." directed at the if statement course.completed, and it doesn't go away unless I delete the whole if statement, I know I'm not fetching the course from anywhere, I just don't know how yet, if there is a better idea/approach please let me know, and thanks for your time.
UPDATE:
Here's a change, this is what I have done so far,
As for the view:
#if ($course->pivot->completed == true)
<button #click.prevent="onSubmit({{ $course }})" type="button" class="btn btn-sm" :id="[isActive ? greenClass.aClass : redClass.aClass]">#{{ greenClass.text }}</button>
{{-- #else
<button #click.prevent="onSubmit({{ $course }})" type="button" class="btn btn-sm" :id="[isActive ? greenClass.aClass : redClass.aClass]"></button> --}}
#endif
Now as for the vue instance:
<script>
new Vue({
el: '#app',
data: {
isActive: true,
greenClass: {aClass: 'coursetogglingtrue', text: 'Done!'},
redClass: {aClass: 'coursetogglingfalse', text: 'Not Yet!'}
},
methods: {
onSubmit: function(course) {
axios.post('/MyCourses/' + course.name)
// .then(function (response){
// });
this.isActive = !this.isActive;
}
}
});
</script>
Since that I know the blade #if condition is passing as true, I can hardcode the is active is true, and when I press on the button I get what I wanted the class actually toggles, if that is not true I default to the other class, now the problem is I need that action to be performed on exactly one button, the one I pressed on, right now what happens is every single button toggles its class, I know that once again it's due to my code not being explicit about this, the thing is I don't know how yet, so please help if you have any idea, have been stuck on this for weeks and weeks, it's really frustrating that I can't get this one simple thing to work.

It doesn’t make sense to put an expression as a property key.
Try this:
new Vue({
el: '#app',
data: {
cssClass: '',
text: ''
},
methods: {
onSubmit: function(course) {
axios.post('/MyCourses/' + course.name)
// .then(function (response){
// });
if (course.completed == true) {
this.cssClass = 'coursetogglingtrue',
this.text = 'Done!'
} else {
this.cssClass = 'coursetogglingfalse',
this.text = 'Not Yet!'
}
}
}
});

Related

How To Display My Invoice Data In Invoice Template

I'm using Laravel 5.7 & VueJs 2.5.* ...
I have invoices table, i need to display specific invoice in a new component, so user can see whatever invoice he wants or print that invoice.
I don't know how to do that, i'm just playing around, if you could help me out, i'll be very grateful to you.
<router-link> to the component
<router-link to="/ct-invoice-view" #click="openInvoice(ctInvoice)">
<i class="fas fa-eye fa-lg text-blue"></i>
</router-link>
Displaying Customer information here like this:
<div class="col-sm-4 invoice-col">
<address v-for="ctInvoice in ctInvoices" :key="ctInvoice.id">
<strong>Customer Info</strong><br>
Name: <span>{{ ctInvoice.customer.customer_name }}</span>
Invoice view component data() & method{}
data() {
return {
ctInvoices: {},
customers: null
};
},
methods: {
openInvoice(ctInvoice) {
axios
.get("api/ct-invoice/show/" + this.viewInvoice)
.then(({
data
}) => (this.ctInvoices = data.data));
},
Image for Better Understanding
You need to look at Dynamic Route matching: https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes
Then you need to use axios.get in invoice views beforeMount function where this.$route.params.id will hold the invoice ID you want to load if the link is applied like so:
<router-link :to="`/ct-invoice-view/${ctInvoice.id}`">
<i class="fas fa-eye fa-lg text-blue"></i>
</router-link>
Alternatively...
I suggest not navigating away from the list, it can be irritating for users having filtered the list then returning to it to look at more invoices and having to filter again unless the filter options and current results are sticky
There are a number of ways of doing this and they are lengthy to example, Typically I would make proper use of a modal and the invoice view load the data on display but to get you started a basic in page solution to experiment with, then try adapting in a reusable modal component later:
<button #click="showInvoice = ctInvoice.id">
<i class="fas fa-eye fa-lg text-blue"></i>
</button>
data() {
return {
loading: false,
invoice: {},
customers: null
};
},
computed: {
showInvoice: {
get: function() {
return this.invoice.hasOwnProperty('id');
},
set: function(value) {
if(value === false) {
this.invoice = {};
return;
}
// could check a cache first and push the cached item into this.invoice else load it:
this.loading = true;
axios.get("api/ct-invoice/show/" + value).then(response => {
// you could push the invoice into a cache
this.invoice = response.data;
}).cache(error => {
// handle error
}).finally(() => {
this.loading = false;
});
}
}
}
In view-invoice component have a close button with bind #click="$emit('close')"
Check this article for how $emit works: https://v2.vuejs.org/v2/guide/components-custom-events.html
<div v-if="loading" class="loading-overlay"></div>
<view-invoice v-if="showInvoice" :invoice="invoice" #close="showInvoice = false" />
<table v-else>....</table>
Hide the table when displaying the invoice, experiment with using v-show instead of v-if upon loosing table content state.
Inside your invoice view, property called invoice will contain the invoice data.
Check this article for how to use props: https://v2.vuejs.org/v2/guide/components-props.html
Hint: The #close listens to the $emit('close')
Could also make use of when switching between table and invoice view.
https://v2.vuejs.org/v2/guide/transitions.html
#MarcNewton
I did something like this, it's working for me, can u just review it for me:
<router-link> to the Invoice View component
<router-link v-bind:to="{name: 'ctInvoiceView', params: {id: ctInvoice.id}}">
<i class="fas fa-eye fa-lg text-blue"></i>
</router-link>
Getting Data of Specific Invoice ID Like This:
created: function() {
axios
.get("/api/ct-invoice/" + this.$route.params.id)
.then(({
data
}) => {
console.log(data);
this.form = new Form(data);
})
.catch(error => {
console.log(error.response);
});
},

Vuejs how to pass data as a prop to a child component

I build following component:
var Modal = Vue.component('modal', {
template: `
<div id="modal" class="modal">
<div class="modal-content">
<p>{{ link }}</p>
</div>
</div>
`,
props: [
'link'
],
});
And I would like to change the link data dynamically after I sent successfully an axios post.
My vue instance
new Vue({
el: '#form',
components: {
'modal': Modal
},
data: {
userId: '',
title: '',
body: '',
snippetLink: '',
},
methods: {
publish (e) {
var self = this;
axios.post('/snippets', {
title: this.title,
body: this.content,
})
.then(function (response) {
console.log("success");
self.link = response.data.hash; // Here I tried to add the reponse content to the vue component's p
})
.catch(function (error) {
console.log(error);
})
},
My Html Markup:
<modal link=""></modal>
...
<button type="button"
v-bind:class="{ 'modal-trigger': !isActiveModal }"
#click="publish">Publish
<i class="material-icons right">send</i>
</button>
So I am sending an axios post to my server successfully and get the data, I would like to open a modal window and put the data in a p tag, so far the modal pops up after my post but I am not sure my it does not change the content of the p tag.
As per my understanding , Snippetlink property can be used to hold data from server.
self.Snippetlink = response.data.hash;
and Pass Snippetlink to link attribute of the snippet-model
<snippet-modal :link="Snippetlink"></snippet-modal>
rupesh_padhye's answer is correct. This is just a further explanation.
First of all, to store the response data to a Vue component, you need to define a key in data for that purpose first. So to make this line work: self.link = response.data.hash;, you need to add link as a key for the Vue component's data:
data: {
userId: '',
title: '',
body: '',
snippetLink: '',
link: ''
},
If you mean snippetLink, change the line to self.snippetLink = response.data.hash;
Secondly, to pass the data as a prop to a child component, you have to specify the prop name, as well as the data key being passed. For instance, to pass your component's link as a prop with the same name to the snippet-modal component, you need: <snippet-modal :link="link"></snippet-modal>

Vue.js with Vuex, and custom components, #click method outputs undefined

I've a menu with about, news and contact buttons in addition to three buttons that allow to change the language of the label on the menu's buttons and the content of the text elements (variable contentText below).
I'm using Vue.js with custom components, as well as Vuex.js to store language states. I'm being able to select the language of the labels on the menu buttons. For instance, if I click on the button labeled fr, the labels on the meny bar change from about to à propos, news to nouvelles, etc., but for some reason that I cannot identify, when I click any one of the menu buttons, the click event doesn't trigger the visibility of the respective text elements. The code that deals with states is something along the following lines (Jsfiddle here):
Vue.use(Vuex)
var storelang = new Vuex.Store({
state: {
lang: {}
},
actions: {
lang: 'code'
},
mutations: {
code(state, ln) {
var jsontext = '{"aboutMenu":"About", "aboutText":"This is just a small text in English.", "newsMenu":"News", "newsText":"News written in the English language.", "contactMenu":"Contact", "contactText":"Contact info written in English."}'
if (ln === 'pt') {
jsontext = '{"aboutMenu":"Sobre", "aboutText":"Isto é um pequeno texto em Português.", "newsMenu":"Novidades", "newsText":"Novidades escritas em Português.", "contactMenu":"Contactar", "contactText":"Informação de contacto escrita em Português."}'
}
if (ln === 'fr') {
jsontext = '{"aboutMenu":"À propos", "aboutText":"Ceci est juste um petit texte en Français.", "newsMenu":"Nouvelles", "newsText":"Des nouvelles écrites en Français.", "contactMenu":"Contacter", "contactText":"Des informations dans la langue Française."}'
}
state.lang = JSON.parse(jsontext)
}
},
strict: true
})
The components with their respective templates, created with Vue.extend:
var contentBtn = Vue.extend({
template: '<button type="button" class="btn btn-default" #click="toggleAbout">{{lang.aboutMenu}}</button><button type="button" class="btn btn-default" #click="toggleNews">{{lang.newsMenu}}</button><button type="button" class="btn btn-default" #click="toggleContact">{{lang.contactMenu}}</button>'
})
var contentTxt = Vue.extend({
template: '<div v-show="aboutIsVisible">{{lang.aboutText}}</div><div v-show="newsIsVisible">{{lang.newsText}}</div><div v-show="contactIsVisible">{{lang.contactText}}</div>'
})
var langBtn = Vue.extend({
template: '<button type="button" class="btn btn-info" #click.prevent=activate("en")>en</button><button type="button" class="btn btn-info" #click.prevent=activate("pt")>pt</button><button type="button" class="btn btn-info" #click.prevent=activate("fr")>fr</button>',
methods: {
activate: function(x) {
storelang.actions.lang(x)
}
},
ready: function() {
return storelang.actions.lang('en') //default language
}
})
And my Vue instance, where I store the values concerning the visiblity of the text elements, register the components and declare the methods for the click events:
new Vue({
el: '#app',
data: {
aboutIsVisible: true,
newsIsVisible: true,
contactIsVisible: true
},
components: {
'langbtn': langBtn,
'contentbtn': contentBtn,
'contenttxt': contentTxt
},
methods: {
toggleAbout: function () {
this.aboutIsVisible = !this.aboutIsVisible
},
toggleNews: function () {
this.newsIsVisible = !this.newsIsVisible
},
toggleContact: function () {
this.contactIsVisible = !this.contactIsVisible
}
}
})
What am I missing?
You're trying to call toggleNews on a child component that doesn't have a method called toggleNews. That method is on the parent component. You'll need to move the button into the parent element, or utilize events to broadcast clicks from the child elements up to the parent.
I moved the child templates up into the parent and your code is working as expected: https://jsfiddle.net/674z6w0h/13/

Pass Vue Variable to Vue Function

I need to pass a VueJS variable to a VueJs function without the page submitting. For some reason the form is still submitting after passing the variable through to the function.
HTML
<div class="list-group-item" v-for="event in events">
<form method="POST" v-on:submit.prevent="deleteEvent('#{{ event.id }}')")>
<b><h1>#{{ event.name }}</h1></b>
<hr>
<small>#{{ event.description }}</small>
<hr>
<b>#{{ event.date }}</b>
<hr>
<button class="btn btn-danger form-control">Delete</button>
</form>
</div>
JavaScript
new Vue({
el: '#app',
data: {
newEvent: {
name: '',
description: '',
date: ''
}
},
ready: function(){
this.retrieveEvents();
},
methods: {
retrieveEvents: function(){
this.$http.get('/retrieveEvents', function(response){
this.$set('events', response);
});
},
addEvent: function(e){
e.preventDefault();
var event = this.newEvent;
this.$http.post('/addEvent', event, function(){
this.newEvent = {name: '', description: '', date: ''};
});
this.retrieveEvents();
},
deleteEvent: function(id){
e.preventDefault();
console.log(id);
}
}
});
I don't see why it keeps submitting the form and not passing the variable into VuejJS, everything else works perfectly.
You don't need to interpolate in the binding syntax, you can access the properties directly:
<form method="POST" v-on:submit.prevent="deleteEvent(event.id)">

Vuejs event on change of element value?

I have an element that I want to watch for a change like this:
<span id="slider-value-upper" class="lower">50</span>
Is it possible to do this cleanly with vuejs? I tried looking in the docs but I could not find anything like this.
I want to launch a custom event whenever '50' changes to something else with VueJs.
Have you tried with watch?
In your case it would be something like this.
template
<div id="app">
{{ message }}
<span id="slider-value-upper" class="lower">{{myValue}}</span><br />
<input v-model="myValue">
</div>
js code
new Vue({
el: '#app',
data: {
message: 'Watch example',
myValue: 50
},
watch: {
'myValue': function(val, oldVal){
if (val < 50) {
this.message= 'value too low!';
}else{
this.message= 'value is ok';
}
}
}
})
check out the example

Categories

Resources