Why does my Vue-router component fails to reload except if loaded from router-link - laravel

I am using vue-router for route navigation in my laravel/Vue.js app. I have a Post component holding individual post of a blog, with router-link tags on excepts of post like so:
<router-link v-bind:to="'/post/' + post.id">
<p class="post_body">{{ post.body | truncate(100) }} </p>
</router-link>
post.id comes from props cascaded down from the parent component, Posts.
The router-link should redirect to another component i called single which will show the single post in details when clicked.
<template>
<div class="single">
<h1>{{ id }}</h1>
</div>
</template>
<script>
export default{
data(){
return {
id: this.$route.params.id
}
},
created(){
console.log(this.id);
}
}
</script>
The single post loads fine. However, when i try to reload/refresh the page, it goes blank. Why does the single component only load when i click from the post component but when i try to reload the page/component, it goes blank (the console also goes blank on refresh).

To expand on #LinusBorg's answer, with Laravel you would define a catch all route to your app.blade.php view file:
Route::get('/{path?}', 'AppController#index')->where('path', '.*');
The controller's action would simply return the view:
// AppController.php
public function index()
{
return view('app');
}

I would assume that you are using history mode but haven't set up the server appropriately.
When using history mode, your web server has to redirect calls to frontend routes (like when you refresh /page/1) to index.html, so your Vue app can boot up and take over the route handling.
Link to the documentation here

Related

How to use paths/urls/routes dynamically in vue.js/laravel components

i'm trying to write my own blog software based on vue.js/laravel for learning purposes.
Background
I'm asking myself how i write vue.js components in which the paths/urls are not hard coded. In the following example i have a post-listing component which lists all posts from the database. The json data is returned by a laravel api route (e.g. /api/posts)
In the listing i use a link to a laravel view (e.g. /posts/{id}) which shows the actual body of a specific post with {id}.
Example
In laravel's api.php route file i can give a name to a specific route and use it with route('api.posts.index'). That's dynamic enough i guess?
api.php
Route::get('', 'Api\ApiPostsController#index')->name('api.posts.index');
index.blade.php
<post-listing postsview="{{ route('web.posts.show') }}" postsapi="{{ route('api.posts.index') }}"></post-listing>
PostListing.vue
In my vue component i refer to these properties postsview and postsapi
<template>
<div>
<h2 class="title is-2">Recent posts</h2>
<ul>
<li v-for="post in posts['data']" v-bind:key="post.id">
<a :href="postsview + '/' + post.slug" v-text="post.title"></a>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ["postsapi", "postsview"],
data() {
return {
posts: []
};
},
methods: {
getPosts() {
axios.get(this.postsapi).then(response => (this.posts = response.data));
}
},
mounted() {
this.getPosts();
}
};
</script>
The question
Is there a "best-practice" way or at least a better approach? Somehow i'm not happy with this solution, but lacking experience, i don't know where to begin.
Thanks.
There are many ways to achive this, this are a few options that I know of.
1: Use blade to pass the route to the component
<component route="{{ route('route_name') }}"></component>
2: You can save a global variable with all the routes you have defined.
You can use Route::getRoutes() to get all the routes
and add it to your window on your front end
3: Use a library,
This library does exactly what you are looking for I think.
https://github.com/tightenco/ziggy
If find other options please let me know, this is a common issue for most laravel developers.

How to set url without refreshing the page (Laravel + vueJS)

I have a Laravel + VueJS app. I would like to change the url when the user selects a new year, for example.
The user in on the url idea/[IDEA_ID]/[YEAR] and changes year inside the page, so i want to set the url, but the rest of the work is done through axios call.
Here is how I work for now:
Route::get('idea/{n}/{m}', 'IdeaController#idea')->name('idea');
class IdeaController extends Controller
{
public function idea($id, $year)
{
$sql = "";
$array = DB::connection('ideas')->select( DB::connection('ideas')->raw($sql));
return view('ideas/idea', ['idea' => json_encode($array)] );
}
}
blade view:
#extends('template')
#section('content')
<div>
<div id="app">
<ideapage ideas="{{ $idea }}"></ideapage>
</div>
</div>
#endsection
And in my Vue view (ideapage), I have all the logic and only make axios requests.
The problem is that I want to change my url inside my Vue view, when the user changes the year for example.
Therefore I am wondering if I did the things well. Would it be a better idea to separate the components inside the laravel blade view? And how can I change only a section when the url changes?
I am not using VueRouter: the routes are in web.php only.
Thanks a lot in advance.
After a lot of thinking, I didn't change my architecture for now, and I only use history.pushstate when the year or idea changes
idea.vue
watch: {
idea: function() {
history.pushState({}, null, '/idea/'+this.idea +'/'+this.year)
},
year: function() {
history.pushState({}, null, '/idea/'+this.idea +'/'+this.year)
}
},

URL route from Laravel to Vue Router (page not found..)

I'm using Laravel 5.4 and vue-js 2.4
Laravel routes
Route::get('/{component?}', 'HomeController#index');
HomeController
public function index()
{
return view('welcome');
}
Vue router
const routes = [
{ path: '/', name: 'myspaces', component: myspaces},
{ path: '/inspiration', component: inspiration },
];
When I click on this, about component is well displayed and the url is example.com/myspaces
<router-link to="/myspaces">myspaces</router-link>
welcome.blade.php
<div id="app">
<v-app>
<v-content>
#Guest
<navigationloggedout></navigationloggedout>
<p>Not logged in</p>
<router-view></router-view>
#else
<navigationloggedin></navigationloggedin>
<p>Logged in</p>
<router-view></router-view>
#endGuest
</v-content>
</v-app>
</div>
Issue
When I enter example.com/myspaces directly. It doesn't actually load the page I want it to, it just loads the welcome.blade.php with the <router-view></router-view>. While I want it to load the specific component myspaces.
How can I find a way to directly reach my vue urls, like they are normal links? Vue normally works with example.com/#/myspaces. You can remove this with mode: history, however you can never actually go to links directly without a 404 error.
Please try the following:
Route::get('/{component?}', function () {
return view('welcome');
})->where('component', '[\/\w\.-]*');
I've just replaced the HomeController#index with an anonymous function for simplicity sake.

Pass data from blade to vue component

I'm trying to learn vue and with that I want to integrate it with laravel too..
I simply want to send the user id from blade to vue component so I can perform a put request there.
Let's say I have this in blade:
<example></example>
How can I send Auth::user()->id into this component and use it.
I kept searching for this but couldn't find an answer that will make this clear.
Thanks!
To pass down data to your components you can use props. Find more info about props over here. This is also a good source for defining those props.
You can do something like:
<example :userId="{{ Auth::user()->id }}"></example>
OR
<example v-bind:userId="{{ Auth::user()->id }}"></example>
And then in your Example.vue file you have to define your prop. Then you can access it by this.userId.
Like :
<script>
export default {
props: ['userId'],
mounted () {
// Do something useful with the data in the template
console.dir(this.userId)
}
}
</script>
If you are serving files through Laravel
Then here is the trick that you can apply.
In Your app.blade.php
#if(auth()->check())
<script>
window.User = {!! auth()->user() !!}
</script>
#endif
Now you can access User Object which available globally
Hope this helps.
Calling component,
<example :user-id="{{ Auth::user()->id }}"></example>
In component,
<script>
export default {
props: ['userId'],
mounted () {
console.log(userId)
}
}
</script>
Note - When adding value to prop userId you need to use user-id instead of using camel case.
https://laravel.com/docs/8.x/blade#blade-and-javascript-frameworks
Rendering JSON
Sometimes you may pass an array to your view with the intention of rendering it as JSON in order to initialize a JavaScript variable. For example:
<script>
var app = <?php echo json_encode($array); ?>;
</script>
However, instead of manually calling json_encode, you may use the #json Blade directive. The #json directive accepts the same arguments as PHP's json_encode function. By default, the #json directive calls the json_encode function with the JSON_HEX_TAG, JSON_HEX_APOS, JSON_HEX_AMP, and JSON_HEX_QUOT flags:
<script>
var app = #json($array);
var app = #json($array, JSON_PRETTY_PRINT);
</script>
Just to add for those who still get error.
For me this <askquestionmodal :product="{{ $item->title }}"></askquestionmodal> still gives error in console and instead showing html page I saw white screen.
[Vue warn]: Error compiling template:
invalid expression: Unexpected identifier in
Coupling to connect 2 rods М14 CF-10
Raw expression: :product="Coupling to connect 2 rods М14 CF-10"
Though in error I can see that $item->title is replaced with its value.
So then I tried to do like that <askquestionmodal :product="'{{ $item->title }}'"></askquestionmodal>
And I have fully working code.
/components/askquestionmodal.vue
<template>
<div class="modal-body">
<p>{{ product }}</p>
</div>
</template>
<script>
export default {
name: "AskQuestionModal",
props: ['product'],
mounted() {
console.log('AskQuestionModal component mounted.')
}
}
</script>

Laravel Sub-menu Within View

Hi I am very new to Laravel and MVC frameworks in general and am looking to create a list of links (in a view within a template) that links to some content. I am using this to display a list of nine people and to display their profile description when the link is clicked on. I have created a model of what the page looks like at http://i.imgur.com/8XhI2Ba.png. The portion that I am concerned with is in blue. Is there a way to route these links to something like /about/link2 or /about?link2 while maintaining the same exact page structure but modifying the ‘link content’ section (on the right of the link menu) to show the specific link's content? I would greatly appreciate it if someone could point me in the right direction, as I have literally no clue where to go with this!
There are a couple ways you can go about doing this.
Templates
Create your route.
Im assuming a lot about your app here but hopefully you get the picture. If you need help with anything in particular, be sure to update your question with the code youve tried so it will be easier to help you.
Route::get('about/{page}', function($page)
{
$profile = Profile::where('name', $page)->first();
return View::make('about')->with('profile', $profile);
});
Modify Template.blade.php
Put this line where you wish for About.blade.php to appear.
#yield('content')
Create your view which will extend your template
#extends('Template')
#section('content')
<h2>User Profile</h2>
<ul>
<li>Name: {{ $profile->name }}</li>
<li>Last Updated: {{ $profile->updated_at }}</li>
</ul>
#stop
AJAX
This solution will utilize AJAX to grab the data from the server and output it on the page.
Route for initial page view
Route::get('about', function($page)
{
$profiles = Profile::all();
return View::make('about')->with('profiles', $profiles);
});
Feel free to follow the same templating structure as before but this time we need to add some javascript into the template to handle the AJAX. Will also need to id everything which needs to be dynamically set so we can easily set it with jquery.
#extends('Template')
#section('content')
<h2>Links</h2>
#foreach($profiles as $profile)
{{ $profile->name }}
#endforeach
<h2>User Profile</h2>
<ul>
<li>Name: <span id="profile_name">{{ $profile->name }}</span></li>
<li>Last Updated: <span id="profile_updated_at">{{ $profile->updated_at }}</span></li>
</ul>
<script>
function setProfile(a)
{
$.ajax({
method: 'get',
url: 'getProfile',
dataType: 'json',
data: {
profile: $(a).data('id')
},
success: function(profile) {
$('#profile_name').html(profile.name);
$('#profile_updated_at').html(profile.updated_at);
},
error: function() {
alert('Error loading data.');
}
});
}
</script>
#stop
Route to handle the AJAX request
Route::get('getProfile', function()
{
$profile_id = Input::get('profile');
$profile = Profile::find($profile_id);
return $profile->toJson();
});
Now, the page should not have to reload and only the profile information should be updated.
Making some assumptions here as no code posted and assuming you're using the latest version of Laravel, Laravel 5.
Lets say you have a table in your database named users and you have a Model named Users (Laravel 5 comes with the Users model as default, see app/Users.php). The users will be the base of our data for the links.
Firstly, you want to register a route so you can access the page to view some information. You can do this in the routes file. The routes file can be found here: app/Http/routes.php.
To register a route add the following code:
Route::get('users', ['uses' => 'UserController#index']);
Now what this route does is whenever we hit the URL http://your-app-name/public/users (URL might be different depending on how you have your app set up, i.e. you may not have to include public) in our web browser it will respond by running the index method on the UserController.
To respond to that route you can set up your UserController as so:
<?php namespace App\Http\Controllers;
class UserController extends Controller {
public function index()
{
}
}
Controllers should be stored in app/Http/Controllers/.
Now lets flesh out the index method:
public function index()
{
// grab our users
$users = App\Users::all();
// return a view with the users data
return view('users.index')->with('users');
}
This grabs the users from the database and loads up a view passing the users data.
Here's what your view could look like:
<!DOCTYPE html>
<html>
<head>
<title>Users Page</title>
</head>
<body>
#foreach($users as $user)
<a href="{{ URL::route('user', ['id' => $user->id]) }}">
{{ $user->username }}
</a>
#endforeach
</body>
</html>
The view code will loop through each user from the $users data we passed to the view and create a link to their user page which is different for each user based on their id (their unique identifier in the DB)
Due to the way I've named it, this would be found in app/views/users/index.blade.php - if you save files ending in blade.php you can use Laravel's templating language, blade.
Now you need to finally set up another route to respond to a user page, for example http://your-app-name/public/user/22.
Route::get('user/{id}', ['uses' => 'UserController#show']);
Then add the show method to UserController
public function show($id)
{
// this will dump out the user information
return \App\User::find($id);
}
Hope that helps a little! Wrote most of it off the top of my head so let me know if you get any errors via comment.
This question is very bare, and it is difficult to actually help your situation without you showing any code. Just to point you in the right direction though, here is what you would need.
A Model called People, this is how you will access your data.
A controller. In this controller you will do the following
Get the ID of the profile you want from the functions parameters
Find that persons information e.g. People::find($person_id);
return the profile view with that persons data e.g. return view('profile')->with('person', $person);
In your view you can then use that data on that page e.g. {{ $person->name }}
For the page that needs to display the links to the people you would have a method in your controller which..
Get all the people data e.g. People::all();
Return a view with that data return view('all-people')->with('people', $people);
You will then need a route to access an individual person. The route will need to pass the persons ID into a controller method e.g.
Route::get('get-person/{id}',
[ 'as' => 'get-person',
'uses' => 'PeopleController#getPerson' ]);
You can then use this route in your view to get the links to each person
#foreach($people as $person)
{{$person->name}}
#endforeach
This would produce the list of links you want.

Resources