Is it possible to get Data back from a vue component?
Laravel blade.php code:
...
<div>
<component1></component1>
</div>
...
In component1 is a selectbox which i need (only the selected item/value) in the blade.php
A vue component, when rendered in the browser, is still valid HTML. If you make sure your component is wrapped in a form element and has a valid input element, and the form can be submitted, the PHP endpoint can consume the formโs data without problems. It could look like this:
Layout/view:
<form method="post" action="/blade.php">
<component1></component1>
<button type="submit">Submit form</button>
</form>
Component (<component1/>):
<fieldset>
<input type="checkbox" name="my_option" id="my_option">
<label for="my_option">I have checked this checkbox</label>
</fieldset>
PHP script (blade.php):
echo $_POST["my_option"] // if checked, should print "on"
If you are looking for a JavaScript centered approach, you may want to serialize the form and fetch the endpoint; it could look similar to this:
import serialize from 'form-serialize';
const formData = serialize(form)
fetch(form.action, { method: 'POST' }, body: JSON.stringify(formData) })
.then(response => {
// update page with happy flow
})
.catch(error => {
// update page with unhappy flow
})
Building from an accessible and standardized basis using proper HTML semantics will likely lead to more understandable code and easier enhancements down the road. Good luck!
(Edit: if you require a complete, working solution to your question, you should post more code, both from the Vue app as well as the PHP script.)
Related
I'm new to Alpine and struggling to wrap my head around how to make a scenario like this work:
Let's say I have a serverside built page, that contains some buttons, that represent newsletters, the user can sign up to.
The user might have signed up to some, and we need to indicate that as well, by adding a css-class, .i.e is-signed-up.
The initial serverside markup could be something like this:
<button id='newsletter-1' class='newsletter-signup'>Newsletter 1</button>
<div>some content here...</div>
<button id='newsletter-2' class='newsletter-signup'>Newsletter 2</button>
<div>more content here...</div>
<button id='newsletter-3' class='newsletter-signup'>Newsletter 3</button>
<div>and here...</div>
<button id='newsletter-4' class='newsletter-signup'>Newsletter 4</button>
(When all has loaded, the <button>'s should later allow the user to subscribe or unsubscribe to a newsletter directly, by clicking on one of the buttons, which should toggle the is-signed-up css-class accordingly.)
Anyway, then I fetch some json from an endpoint, that could look like this:
{"newsletters":[
{"newsletter":"newsletter-1"},
{"newsletter":"newsletter-2"},
{"newsletter":"newsletter-4"}
]}
I guess it could look something like this also:
{"newsletters":["newsletter-1", "newsletter-2", "newsletter-4"]}
Or some other structure, but the situation would be, that the user have signed up to newsletter 1, 2 and 4, but not newsletter 3, and we don't know that, until we get the JSON from the endpoint.
(But maybe the first variation is easier to map to a model, I guess...)
Anyway, I would like to do three things:
Make Alpine get the relation between the model and the dom elements with the specific newsletter id (i.e. 'newsletter-2') - even if that exact id doesn't exist in the model.
If the user has signed up to a newsletter, add the is-signed-up css-class to the corresponding <button> to show its status to the user.
Bind to each newsletter-button, so all of them โ not just the ones, the user has signed up to โ listens for a 'click' and update the model accordingly.
I have a notion, that I might need to 'prepare' each newsletter-button beforehand with some Alpine-attributes, like 'x-model='newsletter-2', but I'm still unsure how to bind them together when Alpine has initialising, and I have the data from the endpoint,
How do I go about something like this?
Many thanks in advance! ๐
So our basic task here is to add/remove a specific item to/from a list on a button click. Here I defined two component: the newsletter component using Alpine.data() creates the data (subs array), provides the toggling method (toggle_subscription(which)) and the checking method (is_subscribed(which)) that we can use to set the correct CSS class to a button. It also handles the data fetching in the init() method that executes automatically after the component is initialized. I have also created a save method that we can use to send the subscription list back to the backend.
The second component, subButton with Alpine.bind() is just to make the HTML code more compact and readable. (We can put each attribute from this directly to the buttons.) So on click event it calls the toggle_subscription with the current newsletter's key as the argument to add/remove it. Additionally it binds the bg-red CSS class to the button if the current newsletter is in the list. For that we use the is_subscribed method defined in our main component.
.bg-red {
background-color: Tomato;
}
<script src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js" defer></script>
<div x-data="newsletter">
<button x-bind="subButton('newsletter-1')">Newsletter 1</button>
<button x-bind="subButton('newsletter-2')">Newsletter 2</button>
<button x-bind="subButton('newsletter-3')">Newsletter 3</button>
<button x-bind="subButton('newsletter-4')">Newsletter 4</button>
<div>
<button #click="save">Save</button>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('newsletter', () => ({
subs: [],
init() {
// Fetch list of subscribed newsletters from backend
this.subs = ['newsletter-1', 'newsletter-2', 'newsletter-4']
},
toggle_subscription(which) {
if (this.subs.includes(which)) {
this.subs = this.subs.filter(item => item !== which)
}
else {
this.subs.push(which)
}
},
is_subscribed(which) {
return this.subs.includes(which)
},
save() {
// Send this.sub to the backend to save active state.
}
}))
Alpine.bind('subButton', (key) => ({
'#click'() {
this.toggle_subscription(key)
},
':class'() {
return this.is_subscribed(key) && 'bg-red'
}
}))
})
</script>
I am vue newbie.
I have a simple form that looks like this:
<form :action=" appUrl +'ConnectionHandler'" method="post" enctype="multipart/form-data">
<fieldset
id="fileHandlingButtons"
:disabled="is_fileHandler_disabled"
>
<legend>File Handling</legend>
<input
type="file"
id="selectFile"
name="selectFile"
>
<input
type="button"
value="Run"
id="run"
#click="startRun"
>
</fieldset>
</form>
I want to submit to my backend without using a submit type. Instead, I want to submit it via ajax.
Vue.js has an official package which is called vue-resource which works as an HTTP client, but the official documentation suggests using Axios.
Installing Axios and Setting up a Component
$ npm install axios
OR
$ yarn add axios
Using Axios in Vue Components
<template>
<div><div/>
</template>
<script>
import axios from "axios";
export default {
data() {
return {};
};
}
</script>
now in your case, your template should look like this
<template>
<div>
<form :action=" appUrl +'ConnectionHandler'" method="post" enctype="multipart/form-data">
<fieldset id="fileHandlingButtons" :disabled="is_fileHandler_disabled" >
<legend>File Handling</legend>
<input type="file" id="selectFile" name="selectFile" >
<input type="button" value="Run" id="run" #click.prevent="startRun">
</fieldset>
</form>
<div/>
</template>
<script>
import axios from "axios";
export default {
data() {
return {};
},
methods: {
startRun() {
axios.post("api endpoint")
.then(response => console.log(response))
}
}
}
</script>
**Note* :* this will not a working code on yours but an idea on how you will solve your problem
To make XMLHTTPRequests from Vue.js you would typically use a library. I recommend Axios, which is also referenced in the Vue guide.
I suggest you start by reading those two, but I've given a short example how you might apply this to your problem below.
Install axios and import it to your component. You would use a package manager like yarn or npm, if you use npm (commonly used) the following command in your project folder should do it:
npm install axios
Define a submitMyForm() method in your component that is going to submit the form for you by making the axios call. here is a (pseudocode) example of what such a method would look like:
submitMyForm() {
axios.post('your-api-url', {
dataField1: value,
dataField2: value
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
Add a listener to whatever user action you would like to submit the form, for example:
<button #click="submitMyForm()">Submit</button>
To have the form-data available in your component methods, you would use v-model bindings, here is a link to the guide entry.. In your case, you might define two variables in the data object of your component, one for each of your input fields. you can then send these variables in your axios.post() call.
There are other patterns to do this, one I personally like is to use Vuex and do all api calls in Vuex actions, organized per module. I prefer this because it enables code reuse between components and removes the responsibility of server communication from my components, keeping them nice and simple. But that's a bit more involved, the above method is good to get started.
I am making a simple put request to my app backend using axios.put();
This all works, I have a button that is binded to vue like #click="submitForm"
However looking around I see that some people still wrap their input fields in forms like those:
<form method="POST" #submit.prevent="onSubmit" action="{{ route('someRoute') }}" id="form-submit">
{{ method_field('PUT') }}
{{ csrf_field() }}
Even if I dont use a form like the one above I get the same result when calling my ajax put request.
Laravel allready adds csfr headers to axios by default in resources/assets/js/bootstrap.js
So is there any reason I still should wrap my inputs in a form like above?
Thanks
Your ajax request doesn't matter if you do your inputs in form tags or not, because the request still sends and receives data from a server.
I would use a form tag because everybody can read the markup much easier and it could be usefull for writing less code in javascript - one example
<form action="" method="">
<div class="form-group">
<label class="control-label">Input</label>
<input type="text" name="input" />
</div>
</form>
$(document).ready(function() {
$('#some-form').on('submit', function() {
var data = $(this).serialize();
... do whatever you want (like ajax call) ...
return false;
});
);
You're using an Ajax request, in which case a standard "form submit" would get prevented. Putting a form around it is not obligatory, especially if you use a button element, which is not a classic form element anyway.
I'm using Laravel 5 to generate a form for a warehouse. In Main form you can select which items to get and it should generate a PDF with the items (all info), people to get the items, date and invoice number.
All the information is on a DIV called 'invoice'. How can I send this object to a new view to generates the PDF. I read about 'invoking dompdf via web' to make it more interactive but the documentation is not clear enough or doesn't fit with I'm needing.
This is my current code:
From:
Recibo.js
$('#createPDF').click(function()
{
$htmlData = "data";
$_token = $('[name="_token"]').val();
$.get('recibos/pdf',{ html:$htmlData, _token: $_token })
.done(function(data)
{
console.log('Done PDF!');
});
});
ReciboController.php
public function reciboPDF(Request $request)
{
$data = $request->get('html');
$pdf = \PDF::loadView('create.template.formReciboPDF',compact('data'))->setPaper('letter')->setOrientation('landscape');
return $pdf->stream();
//return view('create.template.formReciboPDF');
}
formReciboPDF.blade.php
<tr>
<td>{{$data}}</td>
<td>Papeleria</td>
<td>Unidad</td>
<td>Grande</td>
<td>La Palma</td>
<td>2342423424234</td>
<td>Bueno</td>
<td>9</td>
</tr>
Instead of trying to capture the HTML can you send the selection parameters to the server and rebuid the page? Then you would just need to feed the generated HTML into dompdf and render. If visual appearance is important you could use hidden checkboxes as the selection mechanism.
If you want to use a shortcut you can capture the HTML using jQuery.contents().
In your second part it looks like you're trying to send your request via AJAX and get back the PDF. While this is technically possible it's more trouble than it's worth. An easier alternative would be to submit a hidden form to a blank window. It would require some minor changes to your HTML/JS but little else, e.g.
$('#createPDF').click(
function() {
$('#html').val($('#invoice').content());
$(this).closest('form').submit();
});
<form method="POST" action="recibos/pdf" target="_blank">
<input type="hidden" name="html" id="html" value="" />
<input type="hidden" name="_token" value="{value from server}" />
<button type="button" id="createPDF">Create PDF</button>
</form>
Is there a JQuery plugin that allows me to 'unhide' a form by after clicking a link? Like I have an invite link that can take me to a one text field form for an email address but I want this form to just drop down (pushing the rest of the content down also) and shows the form to submit the email. If you guys can think of a JQuery plugin that lets me do this, please let me know
Edit:
So I did this
<div class='add-link'>
<div id='invite_link'><a href=''>Invite User</a></div>
<div id='invitation_form'>
<form>
<input type='text'/>
</form>
</div>
</div>
and my jquery looks like
<script type="text/javascript">
$(function()
{
$("table").tablesorter({sortList:[[0,0],[2,1]], widgets: ['zebra']});
$('#invitation_form').hide();
}
);
$('#invite_link').click(function() {
$('#invitation_form').slideDown();
});
Do you guys see any error that causes the form not to slide down. It hides the form when the page loads but when I click the link it is not sliding down.
$('a.mylink').click(function() {
$('#MyForm').slideDown();
});
I don't think you need a jQuery plugin for this. The base jQuery library should be sufficient.
$('#showFormLink').click(function () {
$('#form').slideDown();
});
If you're looking for animation, that's possible as well by passing in a duration argument to slideDown.
Take a look at the jQuery show documentation.