Laravel Vue
.vue Files setzen ein Webpack-Setup voraus ...
Laravel Default Setup
Vue wird in der Datei resources/assets/js/app.js eingebunden bzw. können durch das Auskommentieren des folgenden CodeBlocks automatisch alle Components des Ordners /resources/js/components geladen werden.
const files = require.context('./', true, /\.vue$/i);
files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default));
Im Standard-Setup wird Vue in der app.js Datei geladen:
const app = new Vue({
el: '#app',
});
Mit diesem Setup reicht es also Vue-Components in /resources/js/components zu hinterlegen:
Als Schreibweise wird hier Pascal case verwendet also sowas wie EditStory.vue
Die Components können in den Bladetemplates dann kleingeschrieben mit Bindestrich eingebunden werden, also
<edit-story></edit-story>
Damit die Javascript-Sourcen in den Blade-Templates verfügbar sind, müssen diese vorher mittels npm gemerged werden:
In der Konsole werden mittels des Befehles:
npm run dev
die Dateien /css/app.css und /js/app.js erstellt.
siehe auch: NPM Pakete - Laravel app.js und css
Während der Entwicklung kann auch "npm run watch" verwendet werden. "Watch" merged die Files bei jeder Änderung des Sourcecodes.
Single File Component - ExampleComponent
Mit Single-File-Component sind die vue-Dateien im Ordner components gemeint, hier ein Beispiel:
/resources/js/components/ExampleComponent.vue
<template>
<div class="container">
<div class="panel-heading">Example Component</div>
<div class="panel-body">
<slot></slot>
</div>
</div>
</template>
Einsatz im Blade-Template:
@extends('layouts.app')
@section('content')
<example-component></example-component>
@endsection
Die Webseite zeigt hier
eine kurze Anmerkung:
Vue wird hier über die Blade-Datei layouts.app geladen:
<head>
...
<!-- Scripts -->
<script src="{{ mix('js/app.js') }}" defer></script>
<!-- Styles -->
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
...
<body>
<div id="app">
...
Vue kann dabei innerhalb von <div id="app"> verwendet werden:
Slot
Der Inhalt des Slot-Bereiches kann innerhalb des Blade-Templates innerhalb der Tags mitgegeben werden:
@extends('layouts.app')
@section('content')
<example-component>SlotText</example-component>
@endsection
Anzeige:
props Parent Child Beispiel
Props dienen dazu Variablen von einem Parent Component in ein Child-Component zu übertragen. Das Child-Component sollte ein Property nie direkt ändern, daher werden Props häufig in den privaten Speicher der Components kopiert:
props: ['value'],
data: function () {
return {
privatevalue: this.value
}
},
Hier ein Beispiel mit 2 Components.
Component 1 bekommt mittels Property die Information welche Daten es per AJAX laden soll, ein Teil der Daten wird dann an Component 2 übergeben. Sollte Component 2 upgedated werden, werden die Änderungen durch den Einsatz von v-model auf Component 1 übertragen.
Wir starten in der routes/web.php-Datei für den spätern AJAX-Call:
Route::get('getstory/{slug}','StoryController@getstory');
Der Call lädt eine Funktion im StoryController, sowas wie:
public function getstory($slug)
{
$story= Story::where('slug',$slug)
->with('hashTags');
return $story;
Im Model (Story.php) ist die Relation dazu hinterlegt:
public function hashTags()
{
return $this->belongsToMany('App\Hashtag')->with('parents:name');
}
Also eine Story und deren Hashtags.
Aufruf der Website: also https://Websiteurl/STORYSLUG
Route::get('/{slug}','StoryController@show')
im StoryController
public function show($slug)
{
return View('stories.show',compact('slug'));
}
<edit-story slug="{{$slug}}">
</edit-story>
An dieser Stelle könnten Anstelle von AXIOS die Daten auch über den StoryController und die View über eine Blade-Variable geladen werden:
<edit-story
model="/story"
:item='{{ json_encode($story) }}'>
</edit-story>
im Component können dann Daten der zuvor erstellten Route via AXIOS geladen werden:
<script>
export default {
props: ['slug'],
data() {
return {
item:[]
};
},
created() {
axios.get('/getstory/' + this.slug)
.then(res => {
this.item= res.data
}).catch(err => {
console.log(err)
})
},
...
Das Template dieser Component kann dann ein anderes Component laden und die Hashtags der zuvorerstellten AJAX-Abfrage übergeben:
<template>
<div>
<hash-tags v-model="item.hashTags"></hash-tags>
</div>
</template>
Die Component hash-tags würde die Daten dann wie folgt laden und z.B. mittles v-for anzeigen:
<template>
<div>
<div v-for="(hashTag, index) in hashtags" :key="index">
<input type="search" v-model="hashtags[index]">
</div>
</div>
</template>
<script>
export default {
props: ['value'],
data: function () {
return { //copy the prop into private component memory
hashtags: this.value
}
},
watch: { //whatch is needed if parent Component loads its data via AXIOS, as otherwise hashtags would not be filled
value: function () {
this.hashtags= this.value
}
},
hideProp Example
Vue.component('example', {
props: ['title', 'body'] ,
data() {
return {
isVisible: true
};
},
template: `
<div class="container" v-show="isVisible">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">
{{ title }}
<button type="button" @click="isVisible = false">X</button>
</div>
<div class="panel-body">
<slot></slot>
</div>
</div>
</div>
</div>
</div>
`
});
<example title="title1">my TestBodyfirstExample</example>
<example title="title2">my TestBodySecondExample</example>
Event Kommunikation
Parent-Child
Child:
this.$emit("create")
Parent:
<add-item @create="reset"></add-item>
methods: {
reset () {
Parent-Child-Input
Parent
<select-image v-model="story.image_id" ></select-image>
Child:
<template>
<div>
<input v-model="image_id" autocomplete="false" class="form-control" v-on:input="$emit('input', $event.target.value)" placeholder="">
</div>
</template>
<script>
export default {
props: ['value'],
data(){
return {
image_id: this.value
}
}
}
</script>
zwischen Components
<script src="/js/app.js"></script>
<script>
window.Event = new Vue();
const app = new Vue({
el: '#app'
})
</script>
zwischen 2 Components:
In z.B. einer method:
Event.$emit("create")
in einem anderen:
created() {
Event.$on('create', () => alert ('event'))
X-Template
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<x-template-component></x-template-component>
</div>
<script src="{{ asset('js/app.js') }}"></script>
<script type="text/x-template" id="x-template-id">
<div class="checkbox-wrapper" @click="check">
Template
</div>
</script>
<script>
Vue.component('x-template-component', {
template: '#x-template-id',
data: function() {
},
methods: {
}
});
const vm = new Vue({
el: '#app'});
</script>
</body>
{{percentage}} % positiv