I'm new to Laravel and Vue and need help implementing Vue-Multiselect.
I don't know how to pass the actual options to the select.
My vue file:
<template>
<div class="dropdown">
<multiselect
:selected.sync="selected"
:show-labels="false"
:options="options"
:placeholder="placeholder"
:searchable="false"
:allow-empty="false"
:multiple="false"
key="name"
label="name"
></multiselect>
<label v-show="showLabel" for="multiselect"><span></span>Language</label>
</div>
</template>
<script>
import { Multiselect } from 'vue-multiselect';
export default {
components: { Multiselect },
props: {
options: {},
placeholder: {
default: 'Select one'
},
showLabel: {
type: Boolean,
default: true
},
selected: ''
}
};
</script>
My blade file:
<div class="form-group">
<drop-down
:options="{{ $members->list }}"
:selected.sync="selected"
:show-label="false"
></drop-down>
</div>
In my controller method I tried a few things:
1.
public function edit($id)
{
....
$members_list = Member::orderBy('member_first_name')->pluck('member_first_name', member_id');
return view('businesses.edit', compact('members_list'));
}
I got this error:
[Vue warn]: Invalid prop: type check failed for prop "options". Expected Array, got Object. (found in component: ).
2.I tried:
$members = Member::orderBy('member_first_name')->pluck('member_first_name', member_id');
$members_list = $members->all();
return view('businesses.edit', compact('members_list'));
I got this error:
htmlspecialchars() expects parameter 1 to be string, array given (View: C:\wamp\www\ccf.local\resources\views\businesses\edit.blade.php)
3.
$members = DB::table('members')
->orderBy('member_first_name', 'asc')
->get();
$members_list = array();
foreach($members as $mem) {
$members_list[$mem->member_id] = $mem->member_first_name;
}
I got this error: htmlspecialchars() expects parameter 1 to be string, array given (View: C:\wamp\www\ccf.local\resources\views\businesses\edit.blade.php)
So I need help with 2 things:
How to send the $members_list as the options
How can I combine the member_first_name and member_last_name fields so I can get options like this:
option value="member_id"
option text = member_first_name member_last_name
Thank you
When using prop binding inside of laravel {{ }} tries to output an escaped form of the variable.
what you need is a javascript array. if $members_list returns a collection, which seems to be indicated by your other code, try
<drop-down
:options="{{ $members_list->toJson() }}"
:selected.sync="selected"
:show-label="false"
></drop-down>
as for your controller this will help
$members = DB::table('members')
->orderBy('member_first_name', 'asc')
->get();
$members_list = $members->map(
function($member) {
return [
"value" => $member->member_id,
"label" => $member->member_first_name. " ". $member->member_last_name
];
}
);
Laravel Collections have a good selection of function that help to manipulate data will map your members array to the structure of { value: "", label: "" } when you convert to Json.
Lastly don't forget to set up your prop bindings in vue.
props: {
options: {
type: Array,
default: function() {
return [];
}
},...
}
That should get you going.
Related
I am working on a task list using Vue.js component and Laravel, with a button to mark each individual task as "complete" or "incomplete". At the moment I can't even get it to change state, let alone maintain it after the page refresh. The console log says [Vue warn]: Error in mounted hook: "TypeError: Assignment to read-only properties is not allowed in strict mode".
CompleteButton.vue
<template>
<button type="button" #click="on_order_button_click()">
{{ buttonText }}
</button>
</div>
</template>
<script>
export default {
props: ['userId', 'item'], required: true,
data() {
return {
item2: this.item
}
},
methods: {
on_order_button_click() {
this.item2.is_complete = !this.item2.is_complete;
localStorage.setItem(this.item2.id, this.item2.is_complete);
}
},
mounted() {
var storedState = localStorage.getItem(this.item2.id);
if (storedState) {
this.item2.is_complete = storedState;
}
},
computed: {
buttonText() {
return this.item2.is_complete === true ? "Completed" : "Incomplete";
}
}
};
</script>
index.blade.php
<complete-button user-id="{{ $user->id }}" item="{{ $item}}"></complete-button>
You are assigning item2 as item prop and which is readonly since it's passed as a property so item2 keeping reference to the same readonly object.
You can simply use spread syntax or Object.assign method to create a new object.
item2: {...this.item}
UPDATE : As you commented, If it's a JSON string then simply parse it ans keep it as item2.
item2: JSON.stringify(this.item)
Employee.vue Component
<tr role="row" class="odd" v-for="(employee, index) in employees" :key="index">
<td>{{ index+1 }}</td>
<td><router-link :to="/employee-profile/+employee.id">{{ employee.name }}</router-link></td>
</tr>
I am sending employee id from here to EmployeeDetails.vue by routes in app.js
let routes = [
{ path: '/employee', component: require('./components/office/Employee.vue').default },
{ path: '/employee-details/:id', component: require('./components/office/EmployeeDetails').default },
]
Here is my EmployeeDetails.vue component
<script>
export default {
data() {
return {
employees:{},
}
},
mounted() {
let id = this.$route.params.id
axios.get('api/employee-details/'+id)
.then(response => {
this.employees = response.data;
});
}
}
</script>
Here is api.php file that I have called route through API resources
Route::get('employee-details/{id}', 'API\EmployeeController#employeeDetails');
and Here is my Controller EmployeeController.php where I have called function for return data
public function employeeDetails($id)
{
return DB::table('employees')->where('id', $id)->get();
}
But the problem is: Data is not showing and return a error message in my console. Error is given below. Actually I want How can I solve this error.
app.js:81221 [Vue warn]: Error in render: "TypeError: Cannot read property '0' of undefined"
found in
---> <EmployeeProfile> at resources/js/components/office/EmployeeProfile.vue
<Root>
app.js:82484 TypeError: Cannot read property '0' of undefined
It seems the id you are passing in the router link is incorrect.
It should be :
<router-link :to="`/employee-profile/${employee.id}`">{{ employee.name }}</router-link>
I'm using laravel 5.6 as the api for the backend and vuejs for the frontend spa application. And https://github.com/nicolaslopezj/searchable package for search which worked well in my previous projects where i used blade templating engine.
I'm new to vuejs and im learning how to do things and im stuck at a point where im confused how to use laravel search on the frontend.
What i did previously in other projects where i used blade for the frontend
In my controller
public function index(Request $request)
{
$start_search = microtime('s');
$searchterm = $request->input('search');
if($request->has('search')){
$results = Web::search($searchterm)->paginate(10);
} else {
$results = Web::latest()->paginate(10);
}
$stop_search = microtime('s');
$time_search = ($stop_search - $start_search);
return view('search.index', compact('results', 'searchterm', 'time_search'));
}
And in my view
<small>{{$results->total()}} results found for "<em class="b-b b-warning">{{$searchterm}}</em>" in {{$time_search}} seconds</small>
#forelse ($results as $result)
<div class="mb-3">
{{$result->meta_title}} <span class="badge badge-pill primary">{{$result->rank}}</span>
<span class="text-success clear h-1x">{{$result->url}}</span>
<span class="h-2x">{{$result->meta_description}}</span>
</div>
#empty
No Results Found
#endforelse
<div class="pt-2 pagination-sm">
{{ $results->links('vendor.pagination.bootstrap-4') }}
</div>
This code worked well where i was able to search and display results properly along with search time.
Now i want to do the same with laravel api and vuejs frontend. So this is what i tried
public function index(Request $request)
{
$start_search = microtime('s');
$searchterm = $request->input('search');
if($request->has('search')){
$results = Web::search($searchterm)->paginate(10);
} else {
$results = Web::latest()->paginate(10);
}
$stop_search = microtime('s');
$time_search = ($stop_search - $start_search);
return WebResource::collection($results);
}
I created a collection resource for the same.
Question 1. My question related to controller code, how to return $searchterm and $time_search so that i can use them on the frontend.
In my vue component, i tried with my learning till now
<template>
other code..
<div class="mb-3" v-for="result in results" :key="result.results">
<router-link :to="result.url" class="text-primary clear h-1x"> {{ result.meta_title }} </router-link>
<span class="h-2x">{{ result.meta_description }}</span>
</div>
</template>
<script>
import axios from 'axios'
import { mapGetters } from 'vuex'
export default {
layout: 'main',
computed: mapGetters({
locale: 'lang/locale',
authenticated: 'auth/check'
}),
metaInfo () {
return { title: this.$t('searchpagetitle') }
},
data: function () {
return {
results: [],
title: window.config.appName
}
},
watch: {
locale: function (newLocale, oldLocale) {
this.getResults()
}
},
mounted() {
console.log(this.locale)
this.getResults ()
},
methods: {
getResults() {
var app = this;
axios.get('/api/web')
.then(response => {
// JSON responses are automatically parsed.
this.results = response.data.data
})
.catch(e => {
this.errors.push(e)
})
}
}
}
</script>
Question 2: My second question related to vuejs, how to create a search form and render results according to the search and with proper search url ending with ?searchterm=searchkeywords in my case.
When i used blade i used the normal html form with action url so that it rendered results. Also i used $searchterm to use the same search terms to search in other routes like images, videos.
I just used {{ url('/images?searchterm='.$searchterm) }} in the href so that when user clicks on it, it renders results of all images with the same keyword just like google. And also used placeholder as "enter your search" and value as "{{$searchterm}}" so that the search term stayed in the search form too.
I want to know how to do all the same in vuejs.
Thank you
It is better to append the search keyword in request URL of API in frontend. So your request url will be like
axios.get('/api/web?searchterm='+this.searchterm)
And in your Laravel controller, you can get this value by using Input::get() , so your code to get searchterm will be
$searchterm = Input::get('searchterm');
And then you can fetch your data on basis of $searchterm variable.
After much Googling and finding the Vue.js forum down, I am ready to give up.
I'm creating a Postcode Lookup component, and everything was working well until I tried to combine it with Laravel's form validation - particularly when there's an error, and the form re-fills the old values.
Hopefully I cover everything here. I have a form input partial that I use which generates every form input. It also uses Laravel's old(...) value if present.
The issue is because there's a default value (in this case for postcode and address) of an empty string, this overrides the value attribute of Postcode input, and the content of the Address textarea.
In made up land, the ideal would be:
data : function() {
return {
postcode : old('postcode'),
address : old('address'),
addresses : [],
hasResponse : false,
selectedAddress : ''
};
},
So that's what I'm trying to replicate.
I can probably replace validation with Ajax validation, but my form partial changes the appearance of fields with an error slightly, so this would be messy
From my understanding:
I can't set an initial data value, as this will override the input value.
I can set a prop, but this is immutable
Any help I can find suggests 'using a computed property which determines its value from the prop' but if you literally do that, it doesn't update.
Here's what I have so far:
<so-postcode-lookup initial-postcode="{{ old('postcode') }}" initial-address="{{ old('address') }}"></so-postcode-lookup>
/**
* Allow user to select an address from those found in the postcode database
*/
Vue.component('so-postcode-lookup', {
name : 'so-postcode-lookup',
template : '#so-postcode-lookup-template',
props : ['initialPostcode', 'initialAddress'],
data : function() {
return {
postcode : '',
address : '',
addresses : [],
hasResponse : false,
selectedAddress : ''
};
},
computed : {
currentAddress : function() {
if (this.address !== '') {
return this.address;
} else {
return this.initialAddress;
}
},
currentPostcode : function() {
if (this.postcode !== '') {
return this.postcode;
} else {
return this.initialPostcode;
}
},
hasAddresses : function() {
return this.addresses.length;
},
isValidPostcode : function() {
return this.postcode !== '' && this.postcode.length > 4;
},
isInvalidPostcode : function() {
return !this.isValidPostcode;
}
},
methods : {
fetchAddresses : function() {
var resource = this.$resource(lang.ajax.apiPath + '/postcode-lookup{/postcode}');
var $vm = this;
var element = event.currentTarget;
// Fetch addresses from API
resource.get({ postcode : this.postcode }).then(function(response) {
response = response.body;
if (response.status == 'success') {
// Update addresses property, allowing select to be displayed
$vm.addresses = response.data;
} else {
$vm.addresses = [];
}
this.hasResponse = true;
});
},
setAddress : function() {
this.address = this.selectedAddress;
}
}
});
<template id="so-postcode-lookup-template">
<div class="row">
#include('partials.input', [
'label' => trans('register.form.postcode'),
'sub_type' => 'postcode',
'input_id' => 'postcode',
'autocorrect' => false,
'input_attributes' => 'v-model="currentPostcode"',
'suffix_button' => true,
'suffix_button_reactive' => trans('register.form.postcode_button_reactive'),
'suffix_text' => trans('register.form.postcode_button'),
'required' => true,
'columns' => 'col-med-50',
'wrapper' => 'postcode-wrapper'
])
<div class="col-med-50 form__item" v-show="hasResponse">
<label for="address-selector" class="form__label" v-show="hasAddresses">{{ trans('forms.select_address') }}</label>
<select id="address-selector" class="form__select" v-show="hasAddresses" v-model="selectedAddress" #change="setAddress">
<template v-for="address in addresses">
<option :value="address.value">#{{ address.text }}</option>
</template>
</select>
<so-alert type="error" allow-close="false" v-show="!hasAddresses">{{ trans('forms.no_addresses') }}</so-alert>
</div>
#include('partials.input', [
'label' => trans('register.form.address'),
'input_id' => 'address',
'type' => 'textarea',
'input_attributes' => 'v-model="currentAddress"',
'required' => true
])
</div>
</template>
If I try this, and set the model of the inputs to currentPostcode and currentAddress respectively, I seem to get an infinite loop.
I think I'm overthinking this somehow.
You can't bind directly to a prop but you can set an initial value using the prop and then bind to that, which is the way to go if you need a two way binding:
Vue.component('my-input', {
props: {
'init-postcode': {
default: ""
}
},
created() {
// copy postcode to data
this.postcode = this.initPostcode;
},
data() {
return {
postcode: ""
}
},
template: '<span><input type="text" v-model="postcode"> {{ postcode }}</span>'
});
Then just do:
<div id="app">
<my-input init-postcode="{{ old('postcode') }}"></my-input>
</div>
Here's the fiddle: https://jsfiddle.net/vL5nw95x/
If you are just trying to set the initial values, but don't need a two way binding, then you can reference the prop directly - as you won't be applying any changes - using v-bind:value:
Vue.component('my-input', {
props: {
'init-postcode': {
default: ""
}
},
template: '<span><input type="text" :value="initPostcode"> {{ postcode }}</span>'
});
And the markup:
Here's the fiddle: https://jsfiddle.net/pfdgq724/
Im working in a easy way to do that using laravel 5.4 controller to send the data directly
In Laravel view:
<input class="form-control" id="ciudad" name="ciudad" type="text" v-model="documento.ciudad" value="{{ old('ciudad', isset($documento->ciudad) ? $documento->ciudad : null) }}" >
in vue.js 2.0
data: {
documento: {
ciudad: $('#ciudad').val(),
},
},
In Laravel Controller
$documento = ReduJornada::where("id_documento",$id)->first();
return view('documentos.redujornada')->with(compact('documento'));
Hello I want to appear the image from code i wrote below but i cant. Any ideas?
I googled and i found that i must use a helper function.
(template)
showallapps.hbs
{{#if getappsexist}}
{{#each app in getapp}}
{{#each app.app_files}}
{{#link-to "dashboard" app}}
<img {{bind-attr src=get_url}} class="img-responsive">
{{/link-to}}
{{/each}}
{{#link-to "dashboard" app}}
{{app.app_name}}
{{/link-to}}
(controller)
showallapps.js
import Ember from 'ember';
export default Ember.ObjectController.extend({
apps:[],
getappsexist: function () {
var appsexist = false;
if (this.store.all('userapp').content.length > 0) {
appsexist = true;
}
return appsexist;
}.property(),
getapp: function () {
this.apps = this.store.all('userapp');
return this.apps;
}.property(),
get_url: function (){
var url = 'http://' + JSON.parse(this.apps.content[2]._data.app_files).url;
return url;
}.property()
});
I have this json.
{
"userapp": [
{
},
{
"app_files": "{"url":"static.xxx.xxx/data/application/3911efd9-413a-11e1-b5e9-fbed80c8f6ba/eleutheris_epilogis.jpg","mime":"image/jpeg","name":"eleutheris epilogis.jpg"}"
}
]
}
I get these errors:
Uncaught Error: Assertion Failed: The value that #each loops over must be an Array. You passed {"url":"static.xxx.xxx/data/application/3911efd9-413a-11e1-b5e9-fbed80c8f6ba/eleutheris_epilogis.jpg","mime":"image/jpeg","name":"eleutheris epilogis.jpg"}
You need to form the image url as a property of some type in your controller (as you did with the getUrl computed property). Then you can bind to that by doing something like this:
<img {{bind-attr src=getUrl}} class="img-responsive" />