Vue Component in Laravel Blade won't render unless renamed - laravel

I have made a Vue component built in a Laravel application for rendering a series of address fields:
<template>
<div>
<label for="address_line_1">Address line 1</label>
<input type="text" id="address_line_1" name="address_line_1" placeholder="Address line 1" :value="addressLine1" #input="setAddressLine1($event.target.value)" :class="{ 'is-invalid': errors.address_line_1 }" required>
</div>
. . .
</template>
<script>
import { mapActions } from "vuex";
export default {
props: {
addressLine1: { type: String },
addressLine2: { type: String },
city: { type: String },
province: { type: String },
postalCode: { type: String },
country: { type: String },
module: { type: String } ,
initErrors: { type: Object },
},
data() {
return {
errors: this.initErrors || {}
}
},
methods: {
...mapActions('collection', ['setAddressLine1', 'setAddressLine2', 'setCity', 'setProvince', 'setPostalCode', 'setCountry']),
},
}
</script>
The file is in a components directory names Address.vue, it's includes in my application templates as:
<address
module="collection"
address-line1="{{ old('address_line_1', session('collection.address_line_1')) }}"
address-line2="{{ old('address_line_2', session('collection.address_line_2')) }}"
city="{{ old('city', session('collection.city')) }}"
province="{{ old('city', session('collection.city')) }}"
postal-code="{{ old('postal_code', session('collection.postal_code')) }}"
country="{{ old('country', session('collection.country')) }}"
:init-errors='#json($errors->messages(), JSON_FORCE_OBJECT)'
></address>
And included in app.js (simplified here)
import Vue from 'vue'
import Address from './components/Address.vue'
const app = new Vue({
el: '#app',
components: { Address },
store: store,
});
require('./bootstrap');
The component does not render on the page at all. There are no JavaScript errors. Other included components are working fine.
However, I have found that if rename all references to the component to e.g. AddressX and then it renders and functions as expected.
Any clues as to why this might be?

Related

My project Vue 3 vuelidate sameAs not wokring

I'm working on a project written in vue 3 and I want to validate it on the login page but sameAs validate not working:
<div class="row">
<label for="password">Şifrə təkrarla</label>
<input
#blur="v$.repassword.$touch()"
v-model.lazy="repassword"
type="text"
name="repassword"
v-bind:class="{ 'is-invalid': !v$.repassword.$invalid }"
placeholder="********"
/>
<small class="validate_message" v-if="!v$.repassword.sameAs.$response"
>Yuxarıda yazdığınız şifrə ilə üst-üstə düşmür.</small
>
</div>
My script:
<script>
import useVuelidate from "#vuelidate/core";
import { sameAs } from "#vuelidate/validators";
export default {
setup() {
return { v$: useVuelidate() };
},
data() {
return {
repassword: "",
};
},
validations() {
return {
repassword: {
sameAs: sameAs(function() {
return this.password;
}),
},
};
},
};
</script>

Laravel vue.js and vuex link body text by id and show in a new component

I am very new to Laravel and Vuex, I have a simple array of post on my page.
test 1
test 2
test 3
I am trying to link the text on the AppPost.vue component and show the post that has been clicked on a new component (AppShowpost.vue) on the same page. I believe I have to get the post by id and change the state? any help would be good. Thank you.
when you click test 1 it will show "test 1" on a new component (AppShowpost.vue)
In side my store timeline.js, I belive I need to get the post by id and change the state ?
import axios from 'axios'
export default {
namespaced: true,
state: {
posts: []
},
getters: {
posts (state) {
return state.posts
}
},
mutations: {
PUSH_POSTS (state, data) {
state.posts.push(...data)
}
},
actions: {
async getPosts ({ commit }) {
let response = await axios.get('timeline')
commit('PUSH_POSTS', response.data.data)
}
}
}
My AppTimeline.vue component
<template>
<div>
<app-post
v-for="post in posts"
:key="post.id"
:post="post"
/>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
computed: {
...mapGetters({
posts: 'timeline/posts'
})
},
methods: {
...mapActions({
getPosts: 'timeline/getPosts'
})
},
mounted () {
this.getPosts()
}
}
</script>
My AppPost.vue component. I need to link the post.body to display the post in my AppShowpost.vue component.
<template>
<div class="w-full inline-block p-4">
<div class="flex w-full">
<p>
{{ post.body }}
</p>
</div>
</div>
</template>
<script>
export default {
props: {
post: {
required: true,
type: Object
}
}
}
</script>
My AppSowpost.vue component that needs to display the post that is clicked.
<template>
<div>
// Displaypost ?
</div>
</template>
<script>
export default {
// Get post from id ?
}
</script>
Okay you can create a new state in your vuex "current_state", asyou said, you can dispatch a mutation by passing the id to the vuex.
state: {
posts: [],
current_state_id : null
},
In your mutations
set_state_id (state, data) {
state.current_state_id = data;
}
On your app post.vue, you can set a computed property that watches the current state
computed: {
currentState() {
return this.$store.getters["timeline/current_state_id"];
}}
And create a watcher for the computed property to display the current id/post
watch: {
currentState: function(val) {
console.log(val);
},
Maybe this will help you. First I will recommend to use router-link. Read about router link here if your interested. It is very helpful and easy to use. But you will have to define the url and pass parameter on our vue-route(see bellow).
1.You can wrap your post.body in router-link as follow.
//With this approach, you don't need any function in methods
<router-link :to="'/posts/show/' + post.id">
{{ post.body }}
</router-link>
2. In your AppSowpost.vue component, you can find the post in vuex state based on url params as follow.
<template>
<div> {{ thisPost.body }} </div>
</template>
// ****************
computed: {
...mapState({ posts: state=>state.posts }),
// Let's get our single post with the help of url parameter passed on our url
thisPost() { return this.posts.find(p => p.id == this.$route.params.id) || {}; }
},
mounted() { this.$store.dispatch("getPosts");}
3. Let's define our vue route.
path: "posts/show/:id",
name: "showpost",
params: true, // Make sure the params is set to true
component: () => import("#/Components/AppShowPost.vue"),
Your Mutations should look as simple as this.
mutations: {
PUSH_POSTS (state, data) {
state.posts = data;
}
},
Please let me know how it goes.

vue-multiselect with both tagging and async, stops working

I'm using vue-multiselect and I would like the user to be able to search tags in the database using async and if they don't find what they want, enter their own tag. This means I'm using tagging and async. It works as expected until I add a tag not found in the , then the aysnc no longer searches. If I remove the added tag, then it does the async search again..
<template>
<div>
<label class="typo__label" for="ajax">Async multiselect</label>
<multiselect v-model="selectedTags" id="tags" label="name" track-by="code" placeholder="Type to search" open-direction="bottom" :options="tags" :taggable="true" #tag="addTag" :multiple="true" :searchable="true" :loading="isLoading" :internal-search="false" :clear-on-select="true" :close-on-select="false" :options-limit="300" :limit="3" :limit-text="limitText" :max-height="600" :show-no-results="false" :hide-selected="true" #search-change="asyncFind">
<template slot="clear" slot-scope="props">
<div class="multiselect__clear" v-if="selectedTags.length" #mousedown.prevent.stop="clearAll(props.search)"></div>
</template><span slot="noResult">Oops! No elements found. Consider changing the search query.</span>
</multiselect>
<pre class="language-json"><code>{{ selectedTags }}</code></pre>
</div>
</template>
<script>
import axios from 'axios';
import Multiselect from 'vue-multiselect'
export default {
components: {
Multiselect
},
props: {
userId: {
type: Number,
required: true
},
tagGroup: {
type: String,
required: true
}
},
data () {
return {
selectedTags: [],
tags: [],
isLoading: false
}
},
methods: {
addTag (newTag) {
const tag = {
name: newTag
}
this.tags.push(tag)
this.selectedTags.push(tag)
},
limitText (count) {
return `and ${count} other tags`
},
asyncFind (query) {
if( query.length > 3 ) {
this.isLoading = true
axios.get('/api/tags/'+this.tagGroup+'/'+query).then(response => {
this.tags = response.data.results.map(a => {
return { name: a.name.en };
});
})
}
},
clearAll () {
this.selectedTags = []
}
}
}
</script>
I'm using the component twice within another component:
<user-tags v-bind:tagGroup="'industry'" :typeahead-activation-threshold="2" :userId="user.id" :isSearchable="true"></user-tags>
<user-tags v-bind:tagGroup="'expertise'" :typeahead-activation-threshold="2" :userId="user.id" :isSearchable="true"></user-tags>

Property or method not defined on instance

I'm trying to create a watcher to bind the value from an input run it through a method and display that value on the page. Its a fairly straightforward sample of code I'm working with but I believe I have a scoping issue but I don't see anything wrong.
I tried changing a few things such as the convertTitle function scoping. e.g. But I got the same error
convertTitle() {
return Slug(this.title)
}
My Vue Component -
<template>
<div class="slug-widget">
<span class="root-url">{{url}}</span>
<span class="slug" v-if="slug !== nil">{{slug}}</span>
</div>
</template>
<script>
export default {
props: {
url: {
type: String,
required: true
},
title: {
type: String,
required: true
}
},
data: function() {
return {
slug: this.convertTitle()
}
},
methods: {
convertTitle: function() {
return Slug(this.title)
}
},
watch: {
title: function(val) {
this.slug = this.convertTitle();
}
}
}
</script>
My Blade Partial with input -
#extends('admin.admin')
#section('content')
<div class="row">
<div class="col-md-9">
<div class="row">
<h1>Create new page</h1>
</div>
<div class="row">
<div class="pcard">
<form action="" method="POST" class="">
{{ csrf_field() }}
<label>Title</label>
<input type="text" name="page-title" placeholder="" v-model="title">
<slug-widget url="{{url('/')}}" :title="title"></slug-widget>
</form>
</div>
</div>
</div>
</div>
#endsection
#section('scripts')
<script>
var app = new Vue({
el: '#app',
data: {
title: ''
}
});
</script>
#endsection
Full error Trace -
[Vue warn]: Property or method "title" 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. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
(found in <Root>)
Updated with compute -
export default {
props: {
url: {
type: String,
required: true
},
title: {
type: String,
required: true
}
},
computed: {
slug() {
return Slug(this.title)
}
}
}
The error was resolved by removing the following snippet from app.js
const app = new Vue({
el: '#app'
});
You shouldn't have, and shouldn't ever need, a function call to supply the value of something in your data.
It sounds like you just need to put convertTitle() into a computed and away you go?

Vue & Laravel: Access and use eventHub

In resources/assets/js/app.js I have created eventHub Vue instance:
require('./bootstrap');
var eventHub = new Vue();
Vue.component('todos-list', require('./components/todos/TodoList.vue'));
Vue.component('todos-add', require('./components/todos/TodoAdd.vue'));
const app = new Vue({
el: '#app'
});
How can I use it in components that are created in separate .vue files?
For example, I also have two components:
todos-list located in /components/todos/TodoList.vue, which is used to fetch the data from server-side using vue-resource:
<template id="todos-list-template">
<div>
<ul v-if="todos.length > 0">
<li v-for="todo in todos">
{{ todo.title }}
</li>
</ul>
<p v-else>You don't hanve any Todos.</p>
</div>
</template>
<script>
export default {
template: '#todos-list-template',
data() {
return {
todos: {}
}
},
mounted() {
this.$http.get('api/vue/todos').then(function(response) {
this.todos = response.data;
});
},
methods: {
handleAddedTodo: function(new_todo) {
this.todos.push(new_todo);
}
},
created: function() {
eventHub.$on('add', this.handleAddedTodo);
},
destroyed: function() {
eventHub.$off('add', this.handleAddedTodo);
}
}
</script>
todos-add located in /components/todos/TodoAdd.vue which is used to add (save) the new 'todo' using vue-resource:
<template id="todos-add-template">
<div>
<form v-on:submit.prevent="addNewTodo()">
<div class="form-group">
<input v-model="newTodo.title" class="form-control" placeholder="Add a new Todo">
</div>
<div class="form-group">
<button>Add Todo</button>
</div>
</form>
</div>
</template>
<script>
export default {
template: '#todos-add-template',
data() {
return {
newTodo: { id: null, title: this.title, completed: false }
}
},
methods: {
addNewTodo() {
this.$http.post('api/vue/todos', { title: this.newTodo.title }).then(function(response) {
if (response.status == 201) {
eventHub.$emit('add', response.data);
this.newTodo = { id: null, title: this.title, completed: false }
}
}, function(response) {
console.log(response.status + ' - '+ response.statusText);
})
}
}
}
</script>
When I add (save) new 'todo' using the todos-add component (TodoAdd.vue) - I want to update data in todos-list component. If I understood well the documentation - for component communication we need to use centralized event hub. And that's what I tried - but I am getting the following error:
eventHub is not defined
I guess because it is defined in app.js, but how can I use in components that are created in separate .vue files?
You are getting this error because eventHub is actually not defined where you are using it. You have to export this from app.js and import in TodoAdd.vue.
in app.js:
var eventHub = new Vue()
exports.eventHub = eventHub
Add this code in TodoAdd.vue:
<script>
import eventHub from '/path/of/app'
export default {
template: '#todos-list-template',
This should make eventHub availble in TodoAdd.vue.
Edited
As the comment suggests, you may consider using vuex, as you have data whcih is being used across components, and going forward I see chances of getting it more complex, more event handlers and event listerners, which can quickly become a nightmare to manage.
var eventHub = new Vue()
You are getting this error because eventHub is actually not defined where you are using it. You have to export this from app.js file. Use this one
window.eventHub = new Vue()

Resources