How to disable personal teams in Laravel / Jetstream when using Inertia? - laravel

I followed an article here:
https://devlan.io/disabling-personal-teams-in-laravel-8-and-jetstream-1fd083593e08
Basically to disable personal teams in Laravel / Jetstream. Now the article gives the example of how to achieve this using Livewire but I am using Inertia. The Livewire template needs this to be changed from this:
#if (Laravel\Jetstream\Jetstream::hasTeamFeatures()
To this:
#if (Laravel\Jetstream\Jetstream::hasTeamFeatures() && Auth::user()->isMemberOfATeam())
How would I achieve this in Inertia? I have had a look around the web, but because I am new to this stack I am completely stumped.
Thanks in advance.

This is a conditional made in Blade templates. Since Inertia doesn't work with Blade, instead it uses some JavaScript framework (Vue, React, Svelte), what you need to do is to run this conditional somewhere in the Laravel side (Controller, Middleware) and set a variable that you'll pass your client side component to conditionally render the teams html stuff.
If you want to have this flag available to all the pages in your app, you can do this through the HadleInertiaRequests middleware.
class HandleInertiaRequests extends Middleware
{
public function share(Request $request)
{
return array_merge(parent::share($request), [
'has_teams' => Laravel\Jetstream\Jetstream::hasTeamFeatures() && Auth::user()->isMemberOfATeam()
]);
}
}
Then, your component, you can use that variable to render the teams part or not (Example using Vue):
<div v-if="$page.props.has_teams">
<!-- Teams... -->
</div>

Related

Laravel Livewire pagination with bootstrap links not displaying

I'm using Laravel Livewire with bootstrap 4. I'm trying to use pagination but the links aren't displaying at the bottom of the table. I don't think the issue is in my blade file or in the livewire component itself, as I can see in the response that there's no pagination links() method available by doing this:
$x = User::paginate(5);
dd($x);
Which yields this result, and contains no reference to links
In my livewire component I'm including the trait: use WithPagination;, and I'm also including the protected $paginationTheme = 'bootstrap'; line as well.
At the bottom of my div just after the table element I have included:
{{ $users->links() }}
Everything I'm doing seems to be "by the book" in terms of what the docs say, but the links just don't display. It's limiting the number of rows on the page as well, just no links.
On your Livewire component:
use Livewire\WithPagination;
Inside class use the trait:
use WithPagination;
Then set the pagination theme:
protected $paginationTheme = 'bootstrap';

Livewire & Laravel 8: updating global components

I’m fairly new to the components & livewire game so I’m getting very confusing when I need to update a component value from other sources. Let me explain:
I’m using a default Laravel 8 installation with Livewire - no JetStream.
My navigation file (the default one that comes with the installation) has 3 individual components containing: total of points achieved, total of lives and remaining lives.
Loads like:
<x-points>0</x-points>
<x-lifes>0</x-lifes>
<x-remaining-lifes>0</x-remainung-lifes>
My question: how do I update any of those components when I execute actions from different sources like:
user answer a question (file Answer.php)
User clicks on an action at the footer of my application (let’s call this Regenerate.php)
User request tips so I need to subtract (Tips.php)
I would change your three main blade components for livewire componentes:
<livewire:points></livewire:points>
<livewire:lifes></livewire:lifes>
<livewire:remaining-lifes></livewire:remaining-lifes>
So for example, let's create the points and answer components.
// Points.php livewire component
public int points = 0;
public $listeners = ['loadUserPoints'];
public function render() { ... }
public function loadUserPoints()
{
$this->points = user()->points()->sum('total');
}
// points.blade.php livewire component
<div wire:init="loadUserPoints">{{ $points }}</div>
Now let's abstract the answer component.
// answer livewire component (very abstracted)
public function save()
{
user()->answers()->create($this->data);
// this will be listened by the points component
// that will run its query and update your frontend
$this->emit('loadUserPoints');
}
So livewire works mainly for events, you have to use it to pass data cross multiple components. To update your frontend as an SPA you're not gonna use blade components, you have to use livewire component or a lot of javascript to handle the DOM.

Using Laravel Inertia.js, Vue and Blade in parallel?

Is there a way to use Laravel Blade for one part of a multipage site (e-commerce), and Inertia/Vue for some specific pages (like the basket and the admin pages)? Not mixing the two on the same pages, as I see it done with other commentaries.
The first category is a load of pages that are merely static, need fast loading and robust SEO referencing (product pages and catalogues). The second do not need to be indexed, but need a lot of user interactions.
I have tried a few things with my first project, but I don’t seem to be able to call Laravel routes when Inertia is active. Plus it would not really make sense to load all Inertia and Vue in the Blade pages. So as a starter I guess I would need to load the Inertia + Vue code only on the Vue pages (admin and basket). And I guess there are a lot of other issues to take care of.
Or maybe scrap Inertia.js, and just load vanilla Vue.js on the Vue pages? But then that means loading the router and the datastore as well...
Many thanks for any idea on the best way to proceed!
E.
You can mix pages as you want:
1. For Inertia pages.
// View:
<inertia-link href="/dashboard">dashboard</inertia-link>
// Laravel controller:
public function index(Request $request)
{
return Inertia::render('Dashboard/Index', [
'data' => [
// ...
],
]);
}
2. Blade pages.
// View:
dashboard
// Laravel controller:
public function index(Request $request)
{
return view('dashboard.index', [
'data' => [
// ...
],
]);
}

Laravel routes with vue/vue router

I'm basically using VueRouter to create all my routes which is working really well. However, my application is built using Laravel. So if I refresh any of the routes I get an error that the route is not defined. So at the minute in every controller I've had to add an index function. This just returns the view of my app.blade which is just the usual tags etc and the to load my single page app. I'm just wondering if there is a cleaner solution? I guess I could move the return view into a single Controller and make my Controllers extend this. But I'm just wondering if there is a better way I'm missing?
i.e. one of my routes via VueRouter:
{
path: "/clients",
name: "clients",
component: () => import(/* webpackChunkName: "clients" */ "../resources/js/components/Views/Clients/Clients.vue")
},
The route in my clients.php file
Route::get('/clients', [App\Http\Controllers\ClientController::class, 'index'])->name('clients');
Then the ClientController index function
public function index()
{
return view('app');
}
It would just be nice to have the loading of the app.blade done somewhere else and not need to be specified per controller. Any help would be appreciated to ensure it's all done efficiently!
Thanks!
Here is how I solved this issue for one of my projects which is also single page application in Vue and Laravel: https://github.com/lyyka/laravel-vue-blog-spa/blob/master/routes/web.php
Simply, in your routes file, you put this code:
Route::get('/{any}', function () {
return view('welcome');
})->where("any", ".*");
And in my welcome view I have:
#extends('layouts.app')
#section('content')
<div class = "container">
<router-view></router-view>
</div>
#endsection
Basically this will return your app view for any URL and so your Vue SPA should work properly. Generally, it is not a good practice to put callback functions inside your routes file, but in this case, you won't even be using your routes file as it is a SPA, so this solution can pass! :)
You should use your single html file and make a controller.
On your controller
public function index(){
return view('index');
}
on your web.php
basically, you should make the same route on your laravel and vue
Route::get('/products', [ProductsController::class,'index']);
in my vue-routes
import Products from './components/Products.vue'
{
path:'/products'
component: Products
}

call to ReactJS component in Laravel blade templates

I use Laravel 5.4 and React 15.5.4, code is writing in ES6.
I'd like replace Vue and use React and I did it. But I often will use small components for example 2 in different places of blade template. I don't want use one app component.
I'd like use something like:
<span class="react">
<TestComponent property={true} />
</span>
I can't do it automatically. Now I use
<span data-component="TestComponent" data-props="{property:true}" />
and in app.js
_.each(document.querySelectorAll('[data-react]'), element => {
let props ={};
Array.prototype.slice.call(element.attributes)
.forEach(item => {
props[item.name] = item.value;
if(item.name !== 'data-react'){
element.removeAttribute(item.name);
}
});
ReactDOM.render(React.createElement(reactComponents[element.getAttribute('data-react')],props),element);
});
It works but I need to use add all properties to one react component property and then use for example this.props.out.propery
I also would like set normal component tag in my blade component
I've try to use in app.js
_.each(document.querySelectorAll('.react'), item => {
ReactDOM.render(item.children,item);
});
Someone have any idea to solve this problem?
EDIT
I changed my solution to:
<span data-react="LoginForm" input="{{json(request()->old())}}" error="{{session('error')}}" errors="{{json($errors->getMessages())}}" />
or
<LoginForm data-react="LoginForm" input="{{json(request()->old())}}" error="{{session('error')}}" errors="{{json($errors->getMessages())}}" />
in blade and in resources/assets/js/app.js
var reactComponents = {
LoginForm: require('./components/login').default,
};
_.each(document.querySelectorAll('[data-react]'), element => {
let props ={};
Array.prototype.slice.call(element.attributes)
.forEach(item => {
props[item.name] = item.value;
});
ReactDOM.render(React.createElement(reactComponents[element.getAttribute('data-react')],props),element);
});
It works fine. This is not super clear solution but I have impression that the reasonable.
I can set components name in html code and add props almost same like in JSX.
As far as I know, you can not mix JSX components directly with Blade templates. The only server side rendering available today for React is NodeJS.
What you could do to improve your architecture is add specific HTML tags with certain ids and render the react components in them. So inside Blade you could do something like:
<div id="componentA"></div>
This will act as a place holder in your Blade template for that react component. Then you render your componentA from your app.js like this:
React.render(<ComponentA prop1='valueX'/>, document.getElementById("componentA"))
Remember that in this case the world of react and world of Blade run at different times.
You could use document.getElementsByTagName('LoginForm') getting all the instances and later iterate its attributes. It's clear code but not generic, because it will work just for LoginForm components.
If you want to render any tag name, then maybe it's better to use some attribute as you used with data-react.
getElementsByTagName isn't super supported by old browsers so maybe could a good idea to use jQuery as fallback $('LoginForm')

Resources