How to get proper data when using blade components and alpinejs? - laravel

I'm new in components and alpine in laravel. I have data from controller $positions.
$positions = [
['id' => 1, 'content' => 'king'],
['id' => 2, 'content' => 'lord']
];
When I pass it to laravel blade. here is my code
<div class="row">
<div class="col">
#foreach($list as $key => $position)
<x-list :position="$position"/>
#endforeach
</div>
</div>
I have component name it <x-list/> with a prop position, and here is the code of my x-list component
<div class="red w-60 mb-1">
<div x-data="positionData()" class="relative">
<button #click="submit()" class="p-2">Submit</button>
</div>
</div>
<script>;
var position = #json($position);
function positionData() {
return {
submit() {
console.log(position);
},
};
}
</script>
It is just very simple code but when I click the submit button, the data I get is the last position
from the list ['id' => 2, 'content' => 'lord'], even I click position 1 the data I get is still position 2 data. I don't know what happen now. I try to search it in google to fix it but I can't find the right answer on this.

I think the issue in this case is that the positionData function is being overwritten on each iteration of x-list (since each of the components is creating a new window.positionData function).
To solve it you could do:
<div class="red w-60 mb-1">
<div x-data="positionData(#json($position))" class="relative">
<button #click="submit()" class="p-2">Submit</button>
</div>
</div>
<script>;
function positionData(position) {
return {
submit() {
console.log(position);
},
};
}
</script>
Specifically, you should probably move the <script></script> out of the x-list component so that it doesn't get re-created for each component (it should only be added to the page once).

Related

laravel livewire return blank page after submit form

Im learning laravel livewire but i encounter this problem, i followed the tutorial in youtube but i dont know what is the problem.
login.blade.php
I have this in form tag wire:submit.prevent="submit"
app.blade.php
#livewireStyles
<livewire:login />
#livewireScripts
Login Component
public $form = [
'username' => '',
'password' => '',
];
public function submit()
{
$this->validate([
'form.username' => 'required',
'form.password' => 'required'
]);
}
public function render()
{
return view('livewire.login');
}
I have same error.
My code is:
#if($errors->any())
<ul>
[laravel error loop]
</ul>
#endif
<div class="anything">
<form wire:submit.prevent="save">
....form tags....
</form>
</div>
It seems livewire not rendering any other html after perfectly closed, like above, after </ul> is closed, then the rest is no rendering
Then I change my code to:
<div class="anything">
#if($errors->any())
<ul>
[laravel error loop]
</ul>
#endif
<form wire:submit.prevent="save">
....form tags....
</form>
</div>
I wrap all elements in one <div>, in this case I use <div class="anything"> And it works!
My form rendered!

How to pass an array from Laravel controller to Vue.js component

I want to pass an array called hours from a Laravel controller to a Vue.js component but for some reason it's not working.
As you can see in the following code, I am trying to create dynamically the options inside a select that is located inside a component but for some reasons I see no options in that array.
In the view I created a <p> to check that the returning array from controller be correct and finally it is correct because in the view I am able to see the second value of the array.
But for some reasons I cannot visualize the values of the array inside the component.
This is my controller code:
$open_at = '00:00';
$close_at = '23:45';
$time = new DateTime($open_at);
$close = new DateTime($close_at);
while ($time < $close) {
$hours[] = $time->format('H:i');
$time->modify('+15 minutes');
}
return view('create')->with('hours', $hours);
This is my view code:
#extends('layouts.app')
#section('content')
<div class="container">
<div id="app">
<create-form :hours="hours"></create-form>
</div>
<p>
{{ $hours[1] }}
</p>
</div>
#endsection
This is code inside the template component:
<div class="form-group">
<label for="start">Start Hour:</label>
<select class="form-control" id="start">
<option v-for="hour in hours" :key="hour.id">
{{ hour }}
</option>
</select>
</div>
This is the my export_default:
export default {
props: ['hours[]'],
mounted() {
console.log('Component mounted.');
this.loadUsers();
this.loadRooms();
},
data: function() {
return {
users: [],
rooms: []
}
},
methods: {
loadUsers: function() {
axios.get('api/users')
.then((response) => {
this.users = response.data.data;
})
.catch(function(error) {
alert('noviva');
console.log(error);
});
},
loadRooms: function() {
axios.get('api/rooms')
.then((response) => {
this.rooms = response.data.data;
})
.catch(function(error) {
alert('noviva');
console.log(error);
});
}
}
}
I visualize the following warning in the console:
Property or method "hours" 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.
Can help?
The definition of your props property doesn't match the name by which $hours is passed to the component
props: ['hours'],
And again you do not have id on hours the way you are constructing the hours[] in controller so the template will give another error when you try :key="hour.id
You must either construct the hours array in controller such that it has an id for every entry or (not recommended) you can use the index in the v-for loop as key
<div class="form-group">
<label for="start">Start Hour:</label>
<select class="form-control" id="start">
<option v-for="(hour, index) in hours" :key="index">
{{ hour }}
</option>
</select>
</div>
PHP arrays and objects can be passed to javascript/vue as props by json encoding them
//Using the blade helper #json
<div id="app">
<create-form :hours='#json($hours)'></create-form>
</div>
//OR using json_encode()
<div id="app">
<create-form :hours="{{ json_encode($hours) }}"></create-form>
</div>

Getting a vue form to go to the next page after submit in laravel

I'm using both laravel and vue and I'm trying to get my vue form to go to another page when I've submitted, but the problem is that once it's submitted it refreshes and goes back to the page it's on instead of the other page.
When I check my network tab in the dev tools, it posted to the right page but it doesn't show up in the browser
This is my vue
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Example Component</div>
<div class="card-body">
I'm an Supplier Code Selection component.
<br>
<form #submit.prevent="submit">
<label for="parent-product">Parent Product</label>
<select name="parent-product" id="parent-product" class="form-control" v-model="selected_parent">
<option>Please select your code</option>
<option v-for="product in products" :value="product.id">
{{ product.supplier_code }}
</option>
<option v-for="child in children" :value="child.id">
{{ child.supplier_code }}
</option>
</select>
<input type="submit" class="btn btn-primary" value="Submit">
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
props: [
'products',
'children',
'selected_parent'
],
mounted() {
console.log('Component mounted.')
},
methods: {
submit(){
var formData = new FormData();
console.log('this is the select box - '+this.selected_parent);
formData.append('parent-product', this.selected_parent);
return axios.post('/add-to-cart/'+this.selected_parent, formData);
},
},
}
</script>
My route
Route::any('/add-to-cart/{id}', 'PublicController#getAddToCart')->name('product.addToCart');
My controller function
public function getAddToCart(Request $request, $id)
{
$menus_child = Menu::where('menu_id', 0)->with('menusP')->get();
$contacts = Contact::all();
$product = Product::find($id);
$supplier_code = $request->supplier_code;
$oldCart = Session::has('cart') ? Session::get('cart') : null;
$cart = new Cart($oldCart);
$cart->add($product, $product->id, $supplier_code);
$request->session()->put('cart', $cart);
return view('public.shopping-cart', ['products' => $cart->items, 'totalPrice' => $cart->totalPrice, 'menus_child' => $menus_child, 'contacts' => $contacts, 'supplier_code' => $supplier_code]);
}
You could just add a location if the axios call returns success. Ps. untested code.
methods: {
submit(){
var formData = new FormData();
console.log('this is the select box - '+this.selected_parent);
formData.append('parent-product', this.selected_parent);
return axios.post('/add-to-cart/'+this.selected_parent, formData)
.then(response => { location: '/'});
},
I found my issue. I forgot to change
return view('public.shopping-cart', compact('supplier_code'));
to
return ['redirect' => route('product.shoppingCart')];
in my laravel controller function

Owl carousel spits out a single item instead of a carousel

I am trying to make a carousel that shows a list of new tutors in a specific area from latest first. I am using laravel 5.6, vue.js and owl carousel.
Below I am using axios to retrieve from the database, and call owl-carousel
<script>
export default {
props: {
areaId: null,
},
data () {
return {
NewTutors: {}
}
},
methods: {
getNewTutor () {
var that = this;
axios.get( '/' + this.areaId + '/home/new').then((response) => {
that.NewTutorss = response.data;
})
.catch(error => {
console.log(error)
this.errored = true
});
}
},
mounted () {
this.getNewTutor();
}
}
$(document).ready(function() {
$('#NewHustles').owlCarousel();
});
</script>
and here and try and loop through each new tutor in a carousel.
<div class="owl-carousel owl-theme owl-loaded" id="NewTutors">
<div class="owl-stage-outer">
<div class="owl-stage">
<div class="owl-item" v-for="NewTutor in NewTutors>
<div class="card">
<div class="card-header">
{{NewTutor.name}}
</div>
<div class="card-body">
<div>
{{NewTutor.area.name}}
</div>
<div>
Image goes here
</div>
<div>
{{NewTutor.user.first_name}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
I get all the cards, with the correct data passed through, but instead of a single row carousel, I get a big blob of cards that move as if there was only one item. I have tried Vue.nexttick, and playing around with a few other things, but nothing seems to work quite right.
Thank you for your help.

Passing data from Vue.js request to Laravel 5.3 form

I have two tables: Countries and Cities. Idea is to show the second dropdown when User has selected country and fill it with cities that correspond to selected country.
Unfortunately I cannot figure out how to pass retrieved values to form.
Values retrieved are below the country with id 21 is Norway :)
Selected { "country": "21", "CountrySelected": true, "cities": { "6": "Oslo", "11": "Lillehammer" } }
// My app.js
const app = new Vue({
el: '#events',
data: {
country:'',
CountrySelected: false,
cities:[],
},
methods: {
WhenCountryHasBeenSelected: function(event) {
this.getCities();
this.CountrySelected= true;
},
getCities: function(){
this.$http.get('api/cities/'+this.country).then(function (response) {
this.cities = response.data;
});
}
},
})
//My api.php routes
Route::get('/cities/{country_id}', function (Request $request, $country_id) {
$cities=App\City::where('country_id', $country_id)->get()->pluck('name','id');
return $cities;
});
//My blade file
#extends('layouts.app')
#section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div id="events">
<div class="panel panel-default">
<div class="panel-heading">Select cities by country</div>
<div class="panel-body">
{!! Form::open() !!}
{!! Form::select('country', $countries, null, ['v-model'=>'country', '#change'=>'WhenCountryHasBeenSelected' ,'class'=>'form-control']) !!}
<div v-show="CountrySelected">
{!! Form::select('city',$cities, null, ['class'=>'form-control']) !!}
{!! Form::close() !!}
Selected #{{ $data | json }}
</div>
</div>
</div>
</div>
</div>
</div>
#endsection
Ended up to use Laravel collection map feature. Mu route looks now like this
Route::get('/cities/{country_id}', function (Request $request, $country_id) {
$collection=App\City::where('country_id', $country_id)->get();
$cities=$collection->map(function($city){
return ['id' => $city->id, 'name' => $city->name];
})->toArray();
Long live Laracast forums and bashy :)

Resources