Use Vue-Router route information to generate page headers - laravel

This is probably really easy and I'm just missing something. I have a vue router page with links that all load up correctly and display correctly.
Hard part out of the way (well it was for me).
I would like to utilise some meta style data attached to each route to provide the page heading title.
So for example:
{
path: '/scenarios',
meta: {
title: "Scenarios!"
},
name: 'Scenarios',
component: Scenarios
}
The route with a meta title.
I would like to pull this data from here within the route declaration
Stick it on a vue page like this:
<template>
<span>
{{ $route.name }}
</span>
</template>
<script>
export default {
name: "PageTitle"
}
</script>
(this actually works to show the route name at the moment)
And I would like to plug it into my template like this:
<h1 class="h2">#yield('page-name')
<page-title></page-title>
</h1>
(I am using Laravel (5.8 I think))
This similar post might help (using a directive) Pass var to vue router in meta but it would be nice to know if its simpler than that. Thoughts?

Screw it, ive answered my own question
changed:
<template>
<span>
{{ $route.name }}
</span>
</template>
to:
<template>
<span>
{{ $route.meta.title }}
</span>
</template>

Related

Laravel Components: default content in {{ slots }}

In old-style Laravel blade templated we used to use #section('section-name') in the following way:
{{-- h1para.blade.php --}}
<h1>
#section('heading')
Heading from Template
#endsection
</h1>
<p>
#yield('details')
</p>
And then extend that template with:
{{-- content.blade.php --}}
#extends('h1para')
#section('details')
Content from content page
#endsection
In the above, my rendered HTML output would look like the following, because the "missing" 'heading' section in the extending file means that we default back to the content in the template:
<h1>Heading from Template</h1>
<p>Content from content page</p>
But in the new components way of doing things, I do:
{{-- .../components/h1para.blade.php --}}
<h1>{{ $heading }}</h1>
<p>{{ $slot }}</p>
In case you haven't gathered, the question is: how do I set a default value for a slot's contents, such that if it isn't supplied in the extending component/template, it falls back to that default?
(I've done my searches, but haven't been able to find the same question asked before)
EDIT:
I should add that I've seen the solution (in the Laravel documentation):
<h1>{{ $heading ?? 'Default Heading Here' }}</h1>
But this seems only to be appropriate if the default value is a short easy to manage string. If the default is a long stream of HTML, then it wouldn't work for my needs.
EDIT 2:
Just to reiterate: the whole point of the question is that the default content could be a long stream of HTML. Solving the problem by passing in a string (be that formatted as HTML or not) wouldn't work for my real-world needs.
I think the solution is this:
{{-- .../component/template.blade.php --}}
<div>
#if (isset($heading))
{{ $heading }}
#else
<h1>Default Heading<span class="subhead">default subheadin and lots of other html content</span></h1>
#endif
<p>{{ $slot }}</p>
</div>
It's not super elegant, but I think it's the only solution. Anyone else have a better answer, I'd love to hear it.
If you pass data like:
<x-h1para header="<span>Header content</span>">
<div>Default slot content here</div>
</x-h1para>
You can display in your component like:
<div>
<h1>{!! $heading ?? 'Default Heading Here' !!}</h1>
{{ $slot }}
</div>

2 way binding with Vue3 Inline template (using slots) in laravel blade template files

In Vue3, inline-templates were depreciated and now slots are used. Is it possible to have 2-way binding of variables for Vuejs components written in blade templates?
I want to have 2-way binding for Vue components that's written inline with blade templates. Although I know I can pass data like <example-component name="Hello World"> It is a ton of work to add props everywhere.
Vue recommends using slots as a inline-template replacement since it got removed in v3, however, that documentation makes no sense. I've got the components displayed using the code below. It's a dead simple text field + paragraph to display the name.
home.blade.php (Removed unnecessary HTML for brevity)
<div>
<h1>Dashboard</h1>
<example-component>
<div class="container">
<input v-model="name" placeholder="Change Name"/>
<p> Name is #{{ name }} </p>
</div>
</example-component>
</div>
example-component.vue
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return {
name: 'hi',
}
}
}
</script>
Unfortunately, this does not work, the name doesn't start as 'hi' and doesn't update when changing the textfield. When I try something like <slot :name=name></slot>. Which I believe would pass the name into the slots section, the component gets rendered for a second before disappearing.
Is having 2-way binding with vue variables in blade templates even possible? Any help is appreciated.
Vue: 3.0.5
Laravel: 8.29.0
Is there a reason you're storing the data in the child component? The reactivity design works by passing props down and emitting events up, even though (unfortunately) the reactivity is not maintained when passing a variable up to the parent component. Seems a little counter intuitive, but I might be missing something in what you're trying to create.
It will, however, work if you put the data into the app instead of the component.
// app
const app = Vue.createApp({
data() {
return {
name: 'hi',
}
}
})
// component
app.component('example-component', {
template: `
<div>
<slot></slot>
</div>`,
})
app.mount("#app");
<script src="https://unpkg.com/vue#3.0.5/dist/vue.global.prod.js"></script>
<div id="app">
<h1>Dashboard</h1>
<example-component>
<div class="container">
<input v-model="name" placeholder="Change Name"/>
<p> Name is #{{ name }} </p>
</div>
</example-component>
</div>
<!--

Component inside the default slot

I am fairly new to Vue and currently am working on the first project with it.
I have encountered this problem which I cannot seem to find a solution for, I have "AuthPage.vue" component which has a slot for the message and default slot for content, once I put custom componen in my login.blade.php it should be prefilled in that slot but it says that custom element does not exist. If I replace slot inside .vue file with custom element it seems to be working perfectly. I am not sure what I am doing wrong.
AuthPage.vue
<template>
<div>
<div class="page-brand-info"></div>
<div class="page-login-main">
<img class="brand-img" src="/assets/global/images/logo.jpg">
<h3 class="font-size-24 text-center"><slot name="title"></slot></h3>
<slot name="message"></slot>
<slot></slot>
<footer class="page-copyright">
<p>© {{ moment().year() }}. All RIGHT RESERVED.</p>
</footer>
</div>
</div>
</template>
<script>
import LoginForm from './LoginForm.vue';
import ResetPasswordForm from './ResetPasswordForm.vue';
export default {
components: { LoginForm, ResetPasswordForm }
}
</script>
login.blade.php
#extends('backoffice.layout.auth')
#section('content')
<auth-page>
<template v-slot:title>Sign In</template>
<login-form></login-form>
</auth-page>
#endsection
Thank you for your help!
Eddie
You'll need to register the LoginForm component globally in order for it to be passed into the AuthPage component as a slot (when passed in directly from a view). This is because, outside the scope of your component, Vue doesn't know what <login-form> is.
Since you're using laravel, in your resources/js/app.js file add the following:
import LoginForm from './path/to/LoginForm.vue';
Vue.component('login-form', LoginForm);

Customize laravel HTML elements

Can laravel built-in html elements be overridden? for example, consider HTML:image tag. I am wondering if I can override it in order to show 'no_available_image.svg' when the given image path doesn't exist.
You can't override an <img> tag (or you shouldn't), but there are other ways to achieve an image fallback.
Also, take in account that HTML:image tag is not a Laravel built-in element, is just HTML and Laravel has nothing to do here.
Blade PHP solution
Check that file exists. If not, it will echo the fallback image.
#if (file_exists(public_path('path/to/image.jpg')))
<img src="{{ asset('path/to/image.jpg') }}">
#else
<img src="{{ asset('images/no_available_image.svg') }}">
#endif
Vue + Blade solution
Following this question, you can create a Vue component like this:
ImgFallback.vue
<template>
<object :data="src" type="image/png">
<img :src="fallback" />
</object>
</template>
<script>
export default {
props: {
src: String,
fallback: String
}
}
</script>
then register it in your app.js
Vue.component('img-fallback', require('./components/ImgFallback.vue'));
So then in your blade templates you can use:
<img-fallback
src="{{ asset('wrong_image_path') }}"
fallback="{{ asset('images/no_available_image.svg') }}">
</img-fallback>
Reusing code
Since you will be using blade and the same fallback image in all cases, you don't want to repeat the fallback attribute everytime, so you can create a blade template called for example image.blade.php and put in the javascript or PHP option. Then in your views call:
#include('image', [ 'path' => 'path/to/your/image.jpg' ])
And use the $path variable to fill the src attribute in the image.blade.php file.
#if (file_exists(public_path($path)))
<img src="{{ asset($path) }}">
#else
<img src="{{ asset('images/no_available_image.svg') }}">
#endif
or
<img-fallback
src="{{ asset($src) }}"
fallback="{{ asset('images/no_available_image.svg') }}">
</img-fallback>

Too many whitespaces when outputting blade variables?

A bit of background: I'm trying to create user-editable pages in a blog-style web application. The application consists of multiple pages worth of content included into one page; dynamically generated from the back-end and laid out one under the other.
Note: I am a Laravel newbie.
The content of the pages is kept in the DB; one DB record = page title + content. I retrieve all records and send them to the template:
class ContentController extends Controller {
var $current_content;
public function __construct() {
$this->current_content = Content::all();
}
public function serveContent() {
return View::make('home')->with('pages', $this->current_content);
}
}
Over in the template I iterate through the object with the freshly retrieved data and display the contents for each page:
#foreach($pages as $page)
<section class="page">
<div class="editable-page" id="{{ $page->page }}">
{{ $page->content }}
</div>
<div hidden class="editable-page-edit-mode" id="{{ $page->page }}">
<textarea class="page-edit">
{{ $page->content }}
</textarea>
</div>
<input hidden type="button" class="edit-btn" value="Edit {{ $page->page }} section"/>
</section>
#endforeach
(The textarea is for the edit mode, it contains the same content as above; except this time it will be sent back to the DB).
The problem is that when outputting the variable contents with {{ $page->content }}, an absurd amount of whitespaces seem to be introduced.
Here's how the section looks in the browser: http://prntscr.com/bzeu4l
And here's what happens behind the scenes: http://prntscr.com/bzets2
Normally it would be completely and utterly irrelevant to me since the content is displayed properly. But when I unhide the textarea, it's clear that it receives the exact same data, with whitespaces: http://prntscr.com/bzeum8
This is a big problem because I don't want the DB values to be overwritten with spaces.
I could work around this by using regex and stripping what has more than 2 spaces when I save the data. But this feels like a workaround, not a solution.
Does anyone know an elegant solution to this? Since I'm a Laravel newbie, it's very possible that I'm missing something obvious about displaying variable values in Blade.
Thanks in advance.
PS: If anybody has any suggestions about my approach / if my approach with the textarea is flawed, I warmly welcome criticism.
Also, I am terribly sorry for the vague layout of the page, it's still in a very incipient stage. In case it's not evident, 'testhomecontent' and 'testservices' are practically $page[0]->content and $page[1]->content.
In blade when all spaces between the contents are preserved. So, to get rid of spaces change your following code
<div hidden class="editable-page-edit-mode" id="{{ $page->page }}">
<textarea class="page-edit">
{{ $page->content }}
</textarea>
</div>
To this:
<textarea class="page-edit">{{ $page->content }}</textarea>
Change
<textarea class="page-edit">
{{ $page->content }}
</textarea>
To
<textarea class="page-edit">{{ $page->content }}</textarea>

Resources