Vuejs dynamically toggle class is not working in Laravel Blade Template - javascript

I am novice in VueJs and As I am trying to implement the basic toggle class functionality using v-bind property of VueJs in my Laravel project. I am not getting the value of variable className while rendering of the page. Please guide me where I am doing wrong. The code is given below:
<div id="root">
<button type="button" v-bind:class="{'className':isLoading}" v-on:click="toggleClass">Toggle Me</button>
</div>
JavaScript is:
<script>
var app = new Vue({
el: '#root',
data: {
className:"color-red",
isLoading:false
},
methods:{
toggleClass(){
this.isLoading=true;
this.className="color-blue";
}
}
})
</script>
Style is:
<style>
.color-red{
background-color:red;
}
.color-blue{
background-color:blue;
}
</style>

You're mixing your approaches slightly. The main issue is in v-bind:class="{'className':isLoading}". This directive, the way you wrote it, toggles a class with the name "className" (literally that, not the value of the variable className) to your element if isLoading is true. If it's false, it doesn't assign any class.
Looking at your code, it seems you're actually trying to set two different classes depending on what the value of isLoading is. The easiest way to do this would be to use v-bind:class="isLoading ? 'color-red' : 'color-blue". Take a look at a working example here.
An even better solution that doesn't pollute your template with logic is to move that expression to a computed property, like this.

You can not have className as well as a variable name, as vue expects it as actual CSS class, documentation suggests one more way, have class object like following:
<script>
var app = new Vue({
el: '#root',
data: {
classObj:{ "color-red" : true } ,
isLoading:false
},
methods:{
toggleClass(){
this.isLoading=true;
this.classObj = { "color-blue" : true};
}
}
})
</script>
<div id="root">
<button type="button" v-bind:class="classObj" v-on:click="toggleClass">Toggle Me</button>
</div>

Related

How do I toggle the contents of an input filed to be encrypted and non encrypted in Vue?

I have an input field and I want the contents to read 'ab****gh' and be able to toggle the contents with a click to read 'abcdefgh'. Basically a reveal and not reveal. I'm having trouble making the value reactive when I change it. Below is some partial code that I've been working with.
Basically i'm trying to swap the content of the input with the encrypted value
Can anyone see where I'm going wrong?
regex_hide_characters: /(?<!^).(?!$)/g,
inputValue: this.value,
encryptedInputValue: this.value.replace(this.regex_hide_characters, '*'),
hidePrivateContent() {
this.reveal = !this.reveal;
if (!this.reveal) {
this.$refs.input.value = this.encryptedInputValue;
}
},
Here is a very basic sample of how you'd achieve something like this:
new Vue({
el: '#app',
computed: {
hiddenPass() {
if (this.isPass) return this.pass.slice(0, 2) + '*******';
return this.pass;
}
},
data() {
return {
isPass: true,
pass: 'abc124defg'
}
},
methods: {}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app" v-cloak>
<div>{{hiddenPass}}</div>
<button #click="isPass=!isPass">toggle view</button>
</div>
In the snippet, I'm taking advantage of computed properties in order to determine, based on data on the instance, how to show the "protected" pass.
I hope this helps!

Vue.js mount component after DOM tree mutation to add a vue component

I have a use case (below) where I need to mount (if thats the correct term) a Vue.js component template that was inserted into the DOM via jQuery, I can setup a Mutation Observer or react to certain events that are triggered when the mutation happens.
I am using Vue.js v2
Here is a simple example I put together to illustrate the point:
live jsFiddle https://jsfiddle.net/w7q7b1bh/2/
The HTML below contains inlined-templates for two components
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<div id="app">
<!-- The use of inline-template is required for my solution to work -->
<simple-counter inline-template>
<button v-bind:style="style" v-on:click="add">clicks: {{ counter }}</button>
</simple-counter>
<simple-counter inline-template>
<button v-on:click="counter += 1">{{ counter }}</button>
</simple-counter>
</div>
<button id="mutate">Mutate</button>
The js:
// simple counter component
Vue.component('simple-counter', {
data: function() {
return {
counter: 0,
style: {
color: 'red',
width: '200px'
}
}
},
methods: {
add: function() {
this.counter = this.counter + 1;
this.style.color = this.style.color == 'red' ? 'green' : 'red';
}
}
})
// create the Vue instance
var initV = () => new Vue({
el: '#app'
});
// expose the instance for later use
window.v = initV();
// click handler that will add a new `simple-counter` template to the Vue.el scope
$('#mutate').click(function(){
$('#app').append(` <div is="simple-counter" inline-template>
<button v-bind:style="style" v-on:click="add">click to add: <span class="inactive" v-bind:class="{ active: true }">{{ counter }}</span></button></div>`)
// do something after the template is incerted
window.v.$destroy()
window.v = initV(); // does not work
})
As mentioned in the code, destroying the re-instantiating the Vue instance does not work, I understand why, the templates for the components are changed on first Vue instantiation to their final HTML, when you try and instantiate a second time, templates are not there, components are not mounted
I'd like to be able to find the newly added components after mutation and mount only those, is that possible? and how?
UPDATE:
I was able to find a way to do it via instantiating a new Vue instance with el set to the specific mutated part of the DOM as opposed to the whole #app tree:
$('#mutate').click(function(){
var appended =
$(`
<div is="simple-counter" inline-template>
<button v-bind:style="style" v-on:click="add">
click to add: {{ counter }}
</button>
</div>`
).appendTo($('#app'));
var newV = new Vue({el: appended[0]});
});
Seems to work, but also looks ugly and I am not sure what other implications this might have..
Use Case:
I am working on a way to write Vue.js components for a CMS called Adobe Experience Manager (AEM).
I write my components using inlined-template which gives me the advantage of SEO as well as server-side rendering using another templating language called HTL.
The way AEM authoring works is that, when a component is edited (via a dialog), that specific component is re-rendered on the server-side then injected back to the DOM to replace the old component, all done via Ajax and jQuery (no browser refresh).
Here is an example
AEM component template:
<button>${properties.buttonTitle}</button>
Here is what an author might do:
author visits the authoring page
opens the button component dialog to edit
changes the buttonTitle to "new button title"
Saves
upon saving, an ajax is sent, the component HTML is re-rendered on the server and returned is the new HTML. That HTML now replaces the old HTML via jQuery (mutates the DOM)
This is fine for static components, but if this was a Vue.js component, how do I dynamically mount it while keeping other components mounted.
An easy solution to this is to refresh the page... but that is just bad experience... There has to be a better way.
Thanks to #liam I was able to find an appropriate solution to my problem
After mutating the DOM with the HTML template, keep a reference to that template's parent element
for example:
var $template = $('<div is="simple-counter" inline-template> ..rest of template here.. <div>').appendTo('#app') // app is the Vue instance el or a child of it
Now you can create a new instance of your component and add $template to it as the el property
if my component was:
var simpleCounterComponent = Vue.component('simple-counter', {
data: function() {
return {
counter: 0,
style: {
color: 'red',
width: '200px'
}
}
},
methods: {
add: function() {
this.counter = this.counter + 1;
this.style.color = this.style.color == 'red' ? 'green' : 'red';
}
}
})
I can do:
var instance = new simpleCounterComponent({
el: $template.get(0) // getting an HTML element not a jQuery object
});
And this way, that newly added template has become a Vue component
Take a look at this fiddle for working example based on the question:
https://jsfiddle.net/947ojvnw/11/
One way to instantiate Vue components in runtime-generated HTML is:
var ComponentClass = Vue.extend({
template: '...',
});
var instance = new ComponentClass({
propsData: { name: value },
});
instance.$mount('#uid'); // HTML contains <... id="uid">
...
instance.$destroy(); // if HTML containing id="uid" is dropped
More here (I am not affiliated with this site)
https://css-tricks.com/creating-vue-js-component-instances-programmatically/

vue.js - dynamic html tag (using variable)

I was wondering if there was any way to dynamically set the tags of an html element. E.g.
var element = "ol";
<{element}> some content </{element}>
Easier method is to just use component element, like this:
<component :is="elType">...</component>
Just set elType in data to whatever type of element you want it to be (ie div, h1 etc)
Demo:
https://codesandbox.io/s/strange-kapitsa-zbtok?fontsize=14&hidenavigation=1&module=%2Fsrc%2FApp.vue&theme=dark
You may want to look into Render Function, jsx is also supported in Vue.js2.
Here's a simple example.
var element = 'ol'
Vue.component('custom', {
render: function (createElement) {
return createElement(
element,
this.$slots.default
)
},
})
new Vue({
el: "#app"
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<custom>abc</custom>
</div>

How to fire function after v-model change?

I have input which I use to filter my array of objects in Vue. I'm using Salvattore to build a grid of my filtered elements, but it doesn't work too well. I think I have to call rescanMediaQueries(); function after my v-model changes but can't figure how.
Here is my Vue instance:
var articlesVM = new Vue({
el: '#search',
data: {
articles: [],
searchInput: null
},
ready: function() {
this.$http.get('posts').then(function (response) {
this.articles = response.body;
});
}
});
And here is how I have built my search
<div class="container" id="search">
<div class="input-field col s6 m4">
<input v-model="searchInput" class="center-align" id="searchInput" type="text" >
<label class="center-align" for="searchInput"> search... </label>
</div>
<div id="search-grid" v-show="searchInput" data-columns>
<article v-for="article in articles | filterBy searchInput">
<div class="card">
<div class="card-image" v-if="article.media" v-html="article.media"></div>
<div class="card-content">
<h2 class="card-title center-align">
<a v-bind:href="article.link">{{ article.title }}</a>
</h2>
<div class="card-excerpt" v-html="article.excerpt"></div>
</div>
<div class="card-action">
<a v-bind:href="article.link"><?php _e('Read More', 'sage'); ?></a>
</div>
</div>
</article>
</div>
I did get the grid system working by adding watch option to my Vue, but every time I wrote something to my input and then erase it my filterBy method wouldn't work at all. It didn't populate any data even if I tried to retype the same keyword as earlier. Here is the watch option I used:
watch: {
searchInput: function (){
salvattore.rescanMediaQueries();
}
}
I think your problem is with the scoping of this in your success handler for http. Your articles object in Vue component is not getting any values from your http.get(..) success handler.
Inside your ready function, your http success handler should be as follows:
this.$http.get('posts').then(response => {
this.articles = response.body; // 'this' belongs to outside scope
});`
Alternatively you can also do:
var self = this; // self points to 'this' of Vue component
this.$http.get('posts').then(response => {
self.articles = response.body; // 'self' points to 'this' of outside scope
});`
Another similar issue: https://stackoverflow.com/a/40090728/654825
One more thing - it is preferable to define data as a function, as follows:
var articlesVM = new Vue({
el: '#search',
data: function() {
return {
articles: [],
searchInput: null
}
},
...
}
This ensures that your articles object is unique to this instance of the component (when you use the same component at multiple places within your app).
Edited after comment #1
The following code seems to work alright, the watch function works flawlessly:
var vm = new Vue({
el: '#search',
template: `<input v-model="searchInput" class="center-align" id="searchInput" type="text" >`,
data: {
searchInput: ""
},
watch: {
searchInput: function() {
console.log("searchInput changed to " + this.searchInput);
}
}
})
The input in template is an exact copy of your version - I have even set the id along with v-model, though I do not see the reason to set an id
Vue.js version: 2.0.3
I am unable to see any further, based on details in the question. Can you check if your code matches with the one above and see if you can get the console debugging messages?
Edited after comment #4, #5
Here is another thought which you need to verify:
Role of vue.js: Render the DOM
Role of salvattore plugin: Make the DOM layouts using CSS only
Assuming the above is true for salvattore plugin, and hopefully it does not mess with vue.js observers / getters / setters, then you can do the following: provide a time delay of about 50 ms so that vue.js completes the rendering, and then call the salvattore plugin to perform the layouts.
So your watch function needs to be as follows:
watch: {
searchInput: function (){
setTimeout(function(){
salvattore.rescanMediaQueries();
}, 50);
}
}
Alternatively you may also use Vue.nexttick() as follows:
Vue.nextTick(function () {
// DOM updated
})
The nextTick is documented here: https://vuejs.org/api/#Vue-nextTick
I do not know if you may need to provide a little bit of extra time for salvattore plugin to start the layouts, but one of the above should work out.
Let me know if it works!

Vue: How do I call multiple functions with #click?

How can I call multiple functions in a single #click? (aka v-on:click)?
So far I tried
Splitting the functions with a semicolon: <div #click="fn1('foo');fn2('bar')"> </div>;
Using several #click: <div #click="fn1('foo')" #click="fn2('bar')"> </div>;
and as a workaround, I can just create a handler:
<div v-on:click="fn3('foo', 'bar')"> </div>
function fn3 (args) {
fn1(args);
fn2(args);
}
But sometimes this isn't nice. What would be the proper method/syntax?
On Vue 2.3 and above you can do this:
<div v-on:click="firstFunction(); secondFunction();"></div>
// or
<div #click="firstFunction(); secondFunction();"></div>
First of all you can use the short notation #click instead of v-on:click for readability purposes.
Second You can use a click event handler that calls other functions/methods as #Tushar mentioned in his comment above, so you end up with something like this :
<div id="app">
<div #click="handler('foo','bar')">
Hi, click me!
</div>
</div>
<!-- link to vue.js !-->
<script src="vue.js"></script>
<script>
(function(){
var vm = new Vue({
el:'#app',
methods:{
method1:function(arg){
console.log('method1: ',arg);
},
method2:function(arg){
console.log('method2: ',arg);
},
handler:function(arg1,arg2){
this.method1(arg1);
this.method2(arg2);
}
}
})
}());
</script>
If you want something a little bit more readable, you can try this:
<button #click="[click1($event), click2($event)]">
Multiple
</button>
To me, this solution feels more Vue-like hope you enjoy
updated dec-2021
you need to separate with a comma
like this:
<button #click="open(), onConnect()">Connect Wallet</button>
to add an anomymous function to do that may be an alternative:
<div v-on:click="return function() { fn1('foo');fn2('bar'); }()"> </div>
Separate into pieces.
Inline:
<div #click="f1() + f2()"></div>
OR: Through a composite function:
<div #click="f3()"></div>
<script>
var app = new Vue({
// ...
methods: {
f3: function() { f1() + f2(); }
f1: function() {},
f2: function() {}
}
})
</script>
This simple way to do
v-on:click="firstFunction(); secondFunction();"
This works for me when you need to open another dialog box by clicking a button inside a dialogue box and also close this one. Pass the values as params with a comma separator.
<v-btn absolute fab small slot="activator" top right color="primary" #click="(addTime = true),(ticketExpenseList = false)"><v-icon>add</v-icon></v-btn>
in Vue 2.5.1 for button works
<button #click="firstFunction(); secondFunction();">Ok</button>
I just want to add one small missing bit here which I felt missing in all of the answers above; that is you actually need to call the method rather than just passing its name as callable, when want to add multiple click handlers.
This might come as a surprise since Vue allows passing a callable to the click handler.
This works
<div><button #click="foo(); bar();">Button1</button></div>
<div><button #click="foo">Button2</button></div>
This does not
<div><button #click="foo; bar;">Button3</button></div>
JsFiddle example
The Vue event handling only allows for single function calls. If you need to do multiple ones you can either do a wrapper that includes both:
<div #click="handler"></div>
////////////////////////////
handler: function() { //Syntax assuming its in the 'methods' option of Vue instance
fn1('foo');
fn2('bar');
}
EDIT
Another option is to edit the first handler to have a callback and pass the second in.
<div #click="fn1('foo', fn2)"></div>
////////////////////////////////////
fn1: function(value, callback) {
console.log(value);
callback('bar');
},
fn2: function(value) {
console.log(value);
}
Html:
<div id="example">
<button v-on:click="multiple">Multiple</button>
</div>
JS:
var vm = new Vue({
el: '#example',
data: {
name: 'Vue.js'
},
// define methods under the `methods` object
methods: {
multiple: function (event) {
this.first()
this.second()
}
first: function (event) {
//yourstuff
}
second: function (event) {
//yourstuff
}
}
})
vm.multiple()
Based on ES6 with anonymous functions:
<button #click="() => { function1(); function2(); }"></button>
You can use this:
<div #click="f1(), f2()"></div>
Simply do like below:
with $event:
<div #click="function1($event, param1); function2($event,param1);"></div>
without $event:
<div #click="function1(param1); function2(param1);"></div>
I'd add, that you can also use this to call multiple emits or methods or both together by separating with ; semicolon
#click="method1(); $emit('emit1'); $emit('emit2');"
You can do it like
<button v-on:click="Function1(); Function2();"></button>
OR
<button #click="Function1(); Function2();"></button>
I was also looking this solution and used different methods and I found this one best for me. Just shared with you
***You can use template literals to use multiple function in one event in vuejs
<div #click="`${firstFunction() ${secondFunction() ${thirdFucntion()}`"></div>
Note:I am using vue3.
you can, however, do something like this :
<div onclick="return function()
{console.log('yaay, another onclick event!')}()"
#click="defaultFunction"></div>
yes, by using native onclick html event.

Categories