VueJS dynamically add textareas based on user click (tinymce issue) - laravel

I built a dynamic form where the user has the option to add as many HTML textareas as possible. The problem I'm running into is when the user loads the next textarea, I lose tinymce. Same thing happening for any external script (example: datepicker and Select2.js). Any ideas?
Here is my code
<div v-for="index in counter" :key="index">
<email_template_row></email_template_row>
<hr>
</div>
<button class="btn btn-sm btn-success" #click="addRow" type="button">Add Row</button>
// Tinymce code here...
// load vuejs from cdn...
Vue.component('email_template_row', {
template: '<div><textarea name="col_2_content[]" class="form-control tinymce" placeholder="Body"></textarea></div>',
});
var vue = new Vue({
el: '#app',
data:{
counter: 1
},
methods: {
addRow: function () {
this.counter += 1;
}
},
});
Thank you.

You need to re-initialize those components after one is appended to the HTML.
So basically your addRow method should look like this:
addRow: function () {
this.counter += 1;
tinymce.init({
selector: 'textarea', // change this value according to your HTML, you can be more specific
auto_focus: 'element',
// add your configuration
});
}

Related

Using Laravel defined routes within a VUE component

I am using Laravel 6 and VUE. I am converting a blade file into a VUE component. The blade file contains a table with rows of data being pulled in from a mysql DB. I have created a draggable component that is displaying users and will allow me to drag and drop rows in order to rearrange them, and the table is using VUE slots to pass html to my component. On my index view the table has a button on it. When the button is clicked it goes to a /create endpoint/route that I defined in Laravel using a routes.php file. I don't want to use vue routing, I prefer to maintain my laravel routes as they are. The issue I am having is, I do not know how to implement this exactly as I am new to VUE. Can someone shed some light on this? I tried using axios but maybe I am mis-using? Is there a better way to use a vue component but continue to use my routes I have defined in routes.php? Thank you.
main.blade.php
<!-- Draggable Component -->
<div id="app">
<draggable table=#yield('content.main')></draggable>
</div>
routes.php (partial to show which endpoint I am wanting to hit)
$route->any('users/create', 'UsersController#create');
Draggable.vue
<template>
<section v-bind:table="table">
<button class="btn btn-default" #click="create()">
<i class="fa fa-plus"></i>
</button>
<slot></slot>
</section>
</template>
<script>
export default {
props: ['table'],
data() {
return {
draggable: '',
};
},
created() {
this.draggable = this.table;
},
methods: {
create() {
axios.get(this.endpoint, {
UserName: this.UserName
}).then(res => {
this.creating = true;
this.UserName = res.data.UserName
}).catch(err => {
console.log("Error: " + err);
})
},
},
computed: {
endpoint() {
return `users/create`;
},
}
};
</script>
The error I get when the button is clicked:
Error: Request failed with status code 404
in your endpoint() you can add window.location.protocol + "//" + window.location.host so the method would be
endpoint() {
return window.location.protocol + "//" + window.location.host + 'users/create';
},

I want to integrate a laravel broadcast to update an html attribute in realtime

HTML FOR PROGRESS BAR
<div class="ui fluid inverted segment" id="app">
<div class="ui small header">ONGOING GRANT ANALYSIS </div>
<div class="ui indicating big progress" id="grantprogress" :data-total="total" :data-value="value" >
<div class="bar">
<div class="progress" ></div>
</div>
</div>
</div>
JS TO ACTIVATE IT
$('#grantprogress').progress();
VUEJS TO LISTEN TO CHANNEL
const app = new Vue({
el: '#app',
created(){
Echo.channel('progress-bar-socket')
.listen('ProgressUpdaterEvent', (e) => {
this.value = e["update"];
console.log(e["update"]);
});
},
data: {
total: 1500000,
value: e["update"]
}
});
I have a Laravel event that is sent when something happens on my website and it updates with some record counts from my database.I update the progress bar with the record counts in realtime. I can get the record counts in my console but binding it as an attribute to the html element of the progress bar is giving me issues.
:data-value="value" is basically saying that the attribute data-value should be set to whatever value is in your data object.
To update the property value you just need to do
this.value = 'the new value of the property';
i.e.
created() {
Echo.channel('progress-bar-socket')
.listen('ProgressUpdaterEvent', (e) => {
this.value = e["update"];
});
},
I would recommend you have a look at The series of tutorials
The value for the progress bar won't automatically update when the attribute changes. Looking at the docs, to update the value you can use:
$('#grantprogress').progress('set progress', this.value);
So, you can either set up a water for value or just put this directly your listener callback:
Echo.channel('progress-bar-socket')
.listen('ProgressUpdaterEvent', e => {
$('#grantprogress').progress('set progress', e.update);
});

Call a Vue.js function inside app.blade.php on button click

Hy Guys,
I need to call a Vue.js function inside app.blade.php file. It should be triggered on a button click.
Here is the code sample I've used.
<button onclick="changeItem()">Saved Items</button>
changeItem() is a Vue.js function.
Kindly help :)
In your app.blade define the component
<div id="app">
<example-component></example-component>
</div>
By defualt laravel will include Vue JS packages in resources\js\app.js
In your resources\js\components\ExampleComponent.vue (Below Code)
<template>
<button v-on:click="say">Say what</button>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
// define methods under the `methods` object
methods: {
say: function (event) {
// `this` inside methods points to the Vue instance
alert('Hello ')
// `event` is the native DOM event
if (event) {
alert(event.target.tagName + " Clicked ")
}
}
}
}
</script>
Run : npm run watch // To compile app.js
Please mark/upvote if this finds helpful :)
<div id="app">
<button #click="changeItem">
{{message}}
</button>
</div>
new Vue({
data: {
message: "Save Items"
},
methods: {
changeItem() {
alert("Clicked!")
}
}
})
#click="changeItem" it is short form v-on:click="changeItem"
Please read this

How i can send ajax request after render vue component?

I have a component
html:
table
tr(is="trcom" v-for="xml in xmls" :xml="xml")
js:
components: {
trcom: {
props: ['xml'],
template: "<tr><td> {{ xml.query }} </td><td> downloading </td><td> downloading </td></tr>",
data: function(){
return {
position: "",
}
}
}
}
can i send ajax request and replace template if ajax is done?
final template:
<tr><td> {{ xml.query }} </td> <td> {{ position }} </td> ...etc... </tr>
Given our discussion in the comments below your question, I have a recommendation:
1) The elements that you're adding and want replaced after individual ajax calls should each be their own component.
2) Because you will be using individual child components, you should use the mounted lifecycle hook to perform your ajax calls.
3) Instead of "replacing" the components' templates, which I'm not sure is even possible, you can instead use conditional rendering to show an initial state vs. a post-ajax state.
Below is a toy example which also uses jQuery (the jQuery itself isn't necessary but is being used for simplicity):
Child Component
Vue.component('my-child-component', {
template: `
<div>
<div v-if="initialized">
I'm initialized! My prop value is: {{my_prop}}
</div>
<div v-else>
I'm not initialized yet!
</div>
</div>
`,
props: ['my_prop'],
data: function() {
return {
initialized: false
};
},
mounted: function() {
//needed because we can't use "this" to reference the vue instance in our ajax call!
var this_vue_instance = this;
//this jQuery ajax call will execute as soon as this component has finished rendering
$.post('/your/target/url', {any_data: 'you might need'}, function(data) {
this_vue.initialized = true;
});
}
});
Root Vue Instance
<script>
$(document).ready(function() {
var root_vue_instance = new Vue({
el: '#app',
data: {
items: [0, 1, 2, 3]
}
});
});
</script>
<div id="app">
<div v-for="item in items">
<my-child-component :my_prop="item"></my-child-component>
</div>
</div>
The above is really bare-bones, but should server as a helpful example for implementing the solution to your current problem.

reCAPTCHA : Grey out Submit button until backend interaction is finished

I've integrated reCAPTCHA and it is working fine, except for when the users are too quick to click the Submit button right after checking the "I'm not a robot" checkbox. It takes quite some time for reCAPTCHA to register the user action via Ajax, and if they click on Submit too quickly, the g-recaptcha-response is missing, and the validation fails.
Hence my question: how to I grey out the Submit button until g-recaptcha-response value is available?
<form id="capform" action="/captchaverify" method="POST">
<div class="g-recaptcha" data-sitekey="..."></div>
<p>
<input id="capsubmit" type="submit" value="Submit">
</form>
I ended up using the data-callback attribute as described in the documentation:
<form action="/captchaverify" method="POST">
<div class="g-recaptcha" data-sitekey="..." data-callback="capenable" data-expired-callback="capdisable"></div>
<p>
<input id="capsubmit" type="submit" value="Submit">
</form>
JavaScript (mootools-based, but the general idea should be clear):
function capenable() {
$('capsubmit').set('disabled', false);
}
function capdisable() {
$('capsubmit').set('disabled', true);
}
window.addEvent('domready', function(){
capdisable();
});
Here's an example that begins with the submit button disabled, and enables it once the callback is received from reCaptcha. It also uses jquery validate to ensure the form is valid before submitting.
var UserSubmitted = {
$form: null,
recaptcha: null,
init: function () {
this.$form = $("#form").submit(this.onSubmit);
},
onSubmit: function (e) {
if ($(this).valid()) {
var response = grecaptcha.getResponse();
if (!response) {
e.preventDefault();
alert("Please verify that you're a human!");
}
}
},
setupRecaptcha: function (key) {
UserSubmitted.recaptcha = grecaptcha.render('recaptcha', {
'sitekey': key,
'callback': UserSubmitted.verifyCallback
//'theme': 'light'//,
//'type': 'image'
});
},
verifyCallback: function (response) {
if (response) {
$(".visible-unverified").addClass("hidden");
$(".hidden-unverified").removeClass("hidden");
}
}
};
I call setupRecaptcha from the page with a named function that's part of the js include.
<script>
var recaptchaLoader = function () {
UserSubmitted.setupRecaptcha("yourkey");
};
</script>
<script src="https://www.google.com/recaptcha/api.js?onload=recaptchaLoader&render=explicit" async defer></script>
You could simplify this. I use it in a multi-tenant application with different keys, and UserSubmitted is actually part of a larger library. You can't usenamespaces (UserSubmitted.somefunction) as the onload param either (to my knowledge).

Resources