در حال بارگذاری ...

Layout Components Overview0:00

So now we've built our own custom form filled with filament, I want to demonstrate how to build your own custom layout component. Now a layout component is essentially a Vue that is able to render other form components. So in this example, I've got a Section component, and this section's built into filament. Now what it does is it renders these six color pickers here, and these are the color pickers that we built previously. The syntax for the section passes in a heading to the make method, then we have our description, we have an icon, and this is the name of a hero icon that we've got installed with the Blade UI kit package. And finally we have this schema, and these are all the child components that get rendered.

Generating a Layout Component0:46

Blade UI kit package. And finally we have this schema, and these are all the child components that get rendered. These are rendered within three columns. The aim of this is going to be to replace this section with our own. It's not going to look exactly the same, but I can demonstrate how to set this sort of component up, and how to render these child components here. So let's make our layout component. I'm going to be using php artisan make:form layout, and this is going to ask for a layout name. So in our case, we're going to be calling it section.

name. So in our case, we're going to be calling it section. Similar to a form field, this will have created a class and a Vue. So the class is in the same place, app/Forms/Components, and the Vue is also in resources/views/forms/components. Now there are a few differences when you generate a layout component to when you generate a form field. The most striking one is going to be the fact that we're extending a different class. So for our colorPicker, we extend Field, and for our section, we're extending Component. Now the difference here is that Field is a component class that has state.

Field vs Component Classes1:57

So for our color picker, we extend Field, and for our section, we're extending Component. Now the difference here is that Field is a component class that has state. So for instance, a text input, a color picker, a select, a date picker, these are all components that have their own state, and they return something when the form is submitted. Now a section is purely visual. It holds together other components, and it doesn't have its own state. And since these two types of form components have different features, we're not extending Field when we're making our own section class. Now we have our Vue property here, which is pointing to the Vue that was created. And also we have this make method.

Now we have our Vue property here, which is pointing to the Vue that was created. And also we have this make method. Now for fields, they already have their make method defined in the base Field class. And this is because all fields accept a name, which is quite predictable. Whereas with layout components, a lot of the time, different components have different parameters for the make method. So for instance, the Section has a heading in the make method. The Grid component has the number of columns within the make method. The Group component has the entire schema in the make method. So these differences mean that we can't have some sort of base make method in the component.

Implementing Heading API3:11

The group component has the entire schema in the make method. So these differences mean that we can't have some sort of base make method in the component class. We have to define it each time we make a new layout component. But this gives us lots of flexibility. So in this case, we're going to be replicating the same API here in this class. So we want a string or a closure to pass in the header. So heading, and we want to pass this to the constructor of the object. And we can do that with the construct method and we can define our property using property promotion.

And we can do that with the construct method and we can define our property using property promotion. So protected string or closure, and we're going to be passing a heading. Now we're going to be using the container to instantiate a new section. And to pass in the heading to the constructor, we can use the second argument to this app method. So heading in here. Now the heading should be set on that property. Finally let's create a getter for the heading. So public function getHeading.

Finally let's create a getter for the heading. So public function getHeading. This is going to be returning a string. As with all other getters, we're going to be evaluating. So this evaluate and passing in our string or closure, and this will unwrap the closure if it exists. Let's go into our demo form and quickly swap out the built-in section with our own section. So section, makeColors, and I'm going to get rid of the two methods here, description and icon that we haven't created yet. Before I go into the browser, I'm going to render out the heading here so we can check.

icon that we haven't created yet. Before I go into the browser, I'm going to render out the heading here so we can check that it's been passed through. So getHeading, as usual, corresponds to the name of the public method in the component class. As you can see, our heading has been output with no styling, and our schema has been output for us as well. And this is handled by the getCharredComponentContainer. Now this will return a component container object, which is internal to filament. You don't really have to worry about it.

Adding Description Support5:27

Now this will return a component container object, which is internal to filament. You don't really have to worry about it. This is a renderable component, and it handles things like the spacing between components, the grid that we've passed through to this columns method. All of that is handled for you, you don't have to worry about rendering the child components. Just echo out this component container and everything's handled for you. So let's now add our description as well. So we want to make a setter for this, so public function description, we're going to be passing in a string, or a closure, or null. Because our description will start off as null, and only be shown if the user actually

in a string, or a closure, or null. Because our description will start off as null, and only be shown if the user actually passes one in. So returning static, which is this, and we're setting our description property to be a description argument here. So let's define our description property, so protected string, or closure, or null description. And the default for this is going to be null. And finally let's copy our heading into a new description getter. So we're going to be evaluating, and this is going to return a nullable string instead of just a string.

So we're going to be evaluating, and this is going to return a nullable string instead of just a string. Now let's output this description as well, in the view, and in the demo form let's uncomment this description line. Cool, so now we have our heading and our description. Let's add some styling to this so that you can tell the difference between the heading and the description. So let's wrap the heading and the description in one div. And then the component container in its own one as well. We could add something like a divider in between the header section and the content section.

Styling and Icon Rendering7:23

And then the component container in its own one as well. We could add something like a divider in between the header section and the content section. So we can do that by passing classes to the attributes bag. This is similar to how a blade component would work. So divide-y, and that will create a horizontal divider. Let's add some padding to each of these containers, so padding-6 potentially. And this one we probably want some spacing, so space-y-6. And let's wrap each of these in an element, so let's use <h2> for this one. And a <p> for the description. For the <h2> I'm going to use text-2xl and font-bold.

And a paragraph for the description. For the h2 I'm going to use text, 2xl and font-bold. And for the paragraph I'm going to use text-small and text-grey-700. I think that will create enough difference. Looking back at this I think space-6 could be quite a lot here, so let's use space-2. As you can see we've got our heading and our description styling. We probably want some sort of border around the entire section like we did in our example. So let's use border, shadow-small. And let's also make it rounded like other filament components, rounded-2xl. So I think 2xl is definitely a Tailwind class, but it's not been compiled into our stylesheet.

And let's also make it rounded like other filament components, rounded 2xl. So I think 2xl is definitely a Tailwind class, but it's not been compiled into our stylesheet. So we need to make sure that we're running Vite. Now there is a slightly different process when you are distributing a plugin with other Tailwind CSS classes in it. And that is to make sure that your users also have those classes compiled. But for now this is just a component that lives in our app, so we don't have to worry about that. Let's check. And it looks like the rounding has been applied perfectly. So finally, let's handle this icon.

And it looks like the rounding has been applied perfectly. So finally, let's handle this icon. So this icon is the name of a Blade UI Kit icon that's been registered. And part of Blade UI Kit is this .svg directive. And it basically takes the name of an icon and some classes that we want to apply to it and then it's going to output it. So let's pass in a GetIconGetter there. And actually, if we have an icon, then we should render the .svg. In our section class, we want to make a new getter, which is going to be our GetIconGetter. We want to make a setter, which is going to be called Icon.

In our Section class, we want to make a new getter, which is going to be our getIconGetter. We want to make a setter, which is going to be called icon. And it's going to be nullable, just like the description is. And finally, we want our icon property up here as well. Now the ID seems to be auto-completing that icon method, so I think everything should be hooked up. As you can see, we do have our icon now. It is in the wrong place though, so let's move this, let's cut it. And this is our header section, so it's got space-y-2, so let's make a new level of nesting here because this is probably going to be aside the heading and the description rather.

And this is our header section, so it's got space-y-2, so let's make a new level of nesting here because this is probably going to be aside the heading and the description rather than above it. Let's make this space-y-2. Let's now make this flex, this container flex-1, and this to have a gap, probably gap-3 or something. Now that was a little bit small, so let's make this bigger, let's make this gap-8, and we can make this text text-gray-500. Now we have our icon that is dynamically rendered. If we swap out our icon to the name of another hero icon that's been registered, like a star, it just updates and everything's properly dynamic.

If we swap out our icon to the name of another hero icon that's been registered, like a star, it just updates and everything's properly dynamic.

Creating a custom form layout class and view using the CLI. Consuming a custom layout in a Filament form. Rendering child form components in a custom form layout component. Defining configuration options for a custom layout using getter and setter methods. Using the Blade Icons package to dynamically render an icon in a view.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟