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

Starting Custom Color Picker0:00

So hopefully now you understand what a Filament component class is and how they work. This is fundamental knowledge for whenever you're going to be building a custom component with Filament. Now we're going to be building a custom form field. The example I've chosen is a color picker. Now Filament does include a color picker in its core by default, but I think this is a good example to demonstrate how to integrate an external JavaScript library. Say we're going to be implementing Iroh.js. The first step to implementing any custom field is to generate the component class and the component view that gets rendered from that class.

Generating Field Class/View0:33

The first step to implementing any custom field is to generate the component class and the component view that gets rendered from that class. So let's use the php artisan make:form-field command, and it's going to ask you what the name of the field is. So in this case, we're going to use colorPicker. Now it's created a ColorPicker class and a color_picker.blade.php view for us. Let's go into that. So colorPicker, this is an App\Forms\Components and the color_picker.blade.php view. And this lives in resources/views/forms/components/color_picker. Now this isn't something we've met before, this view property.

And this lives in resources, views, forms, components, color picker. Now this isn't something we've met before, this view property. And this is implemented on the base ViewComponent class. And this allows us to set a view without having to override the render method of the component. So any view name that we put here gets rendered by this component. Let's put something inside the view so we can check it works. So color. Now let's render this component. So it should be as easy as adding it to a form. I've got a form set up here.

Rendering Field in Form1:37

So it should be as easy as adding it to a form. I've got a form set up here. All it is is a Livewire component that implements the HasForms interface and the InteractiveForms trait. We can use our ColorPicker from App\Form\Components inside the schema. So ColorPicker. That's the one. And we're going to pass in the name of the field here. So I'm just going to call it colorOne. Just so you can see in the demo form view for this Livewire component, we're rendering

So I'm just going to call it colorOne. Just so you can see in the demo form view for this Livewire component, we're rendering out the form, a button to submit the form and the actual form data that lives in Livewire. We can use this to check if the data from the field is getting to the back end correctly. Let's close this. Now in our test form, if I refresh, you can see we've got the name that we put into the field for you here. And we've also got the field's label colorOne. As you can see, the colorOne property on the Livewire component has been initialized.

And we've also got the fields label color one. As you can see, the color one property on the Livewire component has been initialized to be null, but we can always set default for this. So default, and there may be #FF0000. As you can see now, filament's handling the default for us. Now let's actually make an input for this. So just to start with, I'm going to be using a native HTML color picker. So this is the input tag with type color. Let's refresh. And as you can see, we've now got our color picker.

Binding State with Alpine3:05

Let's refresh. And as you can see, we've now got our color picker. When you generate a form field, it's going to give you this, this div with X-data and this state property here. Now this state property is going to be where we store the value of the field in JavaScript before it gets sent to the back end. And this state property is wire:entangle to this string here, this getStatePath. Now how this works is Filament is going to assign each field its own statePath. And this is where the field is going to be storing its state on the back end. So for instance, if we go into the HTML here, we can see that the statePath for this field

And this is where the field is going to be storing its state on the back end. So for instance, if we go into the HTML here, we can see that the state path for this field is data.color1. Now color1 is the name of our field and data is the property on our Livewire component where all of the form data is being stored. Let's close this now. So how we can actually link up this input to the state here is using x-model. This is just a standard Alpine attribute, so state. And this should give us a color picker that is linked to the back end. So as you can see, the default has been filled and it's red.

And this should give us a color picker that is linked to the back end. So as you can see, the default has been filled and it's red. Now if we change this and then submit, you can see that the color has been sent to the back end correctly. So this is all linked up properly. Whenever we choose a new color with our color picker, it's sent to the back end. Now this is working correctly, we can start integrating our external JavaScript library. So as a reminder, this is iro.js. It's going to give us a little color wheel to use and also a shade slider here. Let's get started.

Integrating iro.js Library4:47

It's going to give us a little color wheel to use and also a shade slider here. Let's get started. Okay, so there are two ways to install this. One's from NPM and there's a CDN here. Now usually I would recommend NPM all the way. What I'm actually going to do today though is use the CDN and then in a later episode refactor to using NPM and extracting all of this into a local JavaScript file that can be served from our own web server instead of the CDN. And that's actually going to be using Filament's asset system. So let's just copy this script for now.

And that's actually going to be using Filament's asset system. So let's just copy this script for now. This should do us nicely. So again, this isn't ideal, putting a CDN link inside the blade file. But this is only going to be temporary until we refactor this, so it should be okay. And let's see, they're getting started. So we want a div and we're going to be initializing this color picker on the div and passing in a selector here. But in my experience, it's best not to use IDs like this to initialize a form field. I like to actually use Alpine refs, so that's what we're going to do today.

But in my experience, it's best not to use IDs like this to initialize a form field. I like to actually use Alpine refs, so that's what we're going to do today. So we just need a simple div, we don't need this input anymore. So div, and I'm going to give it an Alpine ref, and a ref is basically a reference to this element that we can then access from the Alpine component. So I'm going to call this Picker, and this should allow us to reference this element. So let's put this on a new line. And we want to run this JavaScript when the field is initialized, so we can use the x-init attribute for that. Let's make some new lines here, let's copy this over.

attribute for that. Let's make some new lines here, let's copy this over. And instead of this selector here, we can try and pass in the actual object. Maybe this will work. Hmm, it hasn't seemed to work, let's have a look. Unexpected token var, oh yeah, I think Alpine doesn't like var very much, let's try const. There we go. So now we have our color picker, but obviously when we submit this doesn't actually do anything because it's not actually linked up to the backend at all. You'll also notice that when we click the submit button, a network request was sent,

because it's not actually linked up to the backend at all. You'll also notice that when we click the submit button, a network request was sent, so a Livewire request, and the color picker actually disappeared, so we need to fix that quickly. The way to do that is to wire:ignore the actual container. This is to make sure that Livewire doesn't end up replacing the JavaScript library with just HTML from the backend. So let's go back here and submit. So now when we submit, the actual color picker stays on the page. So we have our color picker initialized into this const, and how we're going to do this

So now when we submit, the actual color picker stays on the page. So we have our color picker initialized into this const, and how we're going to do this is actually have a look at some events. So potentially this color picker is going to be emitting an event each time that the color's changed, and we can listen for that event and update this state property here with a new color. So let's have a look, see if there's any events. So working with colors, color picker events, here we go. So colorPicker on, and then the event name, and then something that runs when the event is triggered.

So colorPicker on, and then the event name, and then something that runs when the event is triggered. Okay, so colorPicker.on, and the event name here is going to be color:change. So it should run when we pick a new color, and the second is going to be a callback function, so accept color, and this is going to run. So color.hexString is how we get our hex string. So let's use this here. So color.hexString, and for now I'm just going to console.log this so we can see when it's actually being triggered. Maybe it gets triggered when we defocus, or maybe it gets triggered instantly.

it's actually being triggered. Maybe it gets triggered when we defocus, or maybe it gets triggered instantly. Let's have a look. So Inspector, console, okay. So it gets triggered exactly when we actually move the color picker here. That's fine. That's alright. At least it's now all hooked up. So let's set the state property to the colorHexString. So state equals colorHexString.

So let's set the state property to the color hex string. So state equals color hex string. Okay, so let's pick green, and submit. Perfect, so we now have our green hex color here that's been sent to the backend, and this works because when the color gets changed, we are setting the alpine property to the new color, and then because this alpine property is entangled to Livewire, it's sending our new value. Now one thing I did notice was even though our default is FF0000, this is not red. So we need to work out how to pass in a default value or a value when it actually gets initialized. So let's go back into the documentation, okay, available options, here we go.

So we need to work out how to pass in a default value or a value when it actually gets initialized. So let's go back into the documentation, okay, available options, here we go. So color, the initial color value can be any supported color format, so yeah, it can be a hex string. So how do we pass in the options here? Okay, there's a second argument to iro.colorpicker. So let's add that in, and it's going to be color. And obviously the default is actually stored still in this alpine state property, so we should just be able to do state. And then when we reload, it should be red by default, there we go.

Adding Configurable Width11:02

should just be able to do state. And then when we reload, it should be red by default, there we go. And we can still do this, select green, and then submit, perfect. Back to red, submit, yep. Okay, that's pretty good. I'm going to be now trying to add some sort of config option to this color picker to demonstrate how we actually pass that in, very similar to our text input demo. So let's have a look at what options we have to play with. So for example, let's have a look at making a width method on the color picker. So we can pass in something like width, and then 100, and then that changes it from 320.

So for example, let's have a look at making a width method on the color picker. So we can pass in something like width, and then 100, and then that changes it from 320 to be 100 instead. So how we do this is we make our public function width on our field instance, we pass in our int of width, we're going to be returning this. Some type of this should probably be static, so that if someone inherits this color picker, then we're going to be returning the correct child class instead of the current class. And we're going to be setting a property to be the width, so this width equals width. Now one oversight I did make was this should probably also accept a closure, so that we can evaluate it.

Now one oversight I did make was this should probably also accept a closure, so that we can evaluate it. And potentially the default should be null, and then only if we pass in a width should we actually pass that into the JavaScript library, else it should dictate how wide it should be. So let's make our property, so protected int closure or null width, and the default for this is going to be null. And then we need our getter, so public function get width, int closure, null. And we can return this evaluate, and evaluate the width, and actually because we're evaluating here we don't need this in our return type, because it's always going to be evaluated.

And we can return this evaluate, and evaluate the width, and actually because we're evaluating here we don't need this in our return type, because it's always going to be evaluated. Okay so now if we go back to here, okay we've got our ID equal to completion, this is going through correctly, so that looks like it's fine. Just to test, I'm going to just echo out the width here, so since all of the public methods are callable from the view, with our variable syntax, we can do getWidth. And now we should see above our colour picker, a hundred, perfect. So as you can see, the configuration value from our form has been passed straight into our view, and we can now just pass it straight to the JavaScript library as well. So how should we do this?

our view, and we can now just pass it straight to the JavaScript library as well. So how should we do this? So this is just a simple width property right, yeah, okay. So I'm just going to use an if statement here. So if the width has been set, then set a width property here, and we're going to pass this through using JS. This just ensures that everything is formatted correctly for JavaScript. Technically it doesn't really matter here because we're just outputting a number, but say if we wanted to output an object for a different configuration value, JS is going to protect us from some of the escaping issues that you might encounter.

say if we wanted to output an object for a different configuration value, JS is going to protect us from some of the escaping issues that you might encounter. And I find it's quite good to be consistent with this sort of thing and just use JS everywhere. So now let's go back on here. Oh that doesn't, that looks weird, looks like we've lost quite a lot. What's happening? Oh, I think I know what's happening here. Because we're using the width variable and not getWidth, we actually need to save the getWidth first. The reason I'm not going to call getWidth both times is because then if it's a closure

getWidth first. The reason I'm not going to call getWidth both times is because then if it's a closure it's going to run twice, so that isn't very performant. So usually I like to save the configuration values in like a php block here. So width equals getWidth. And now the width should evaluate, and this should be an int or null, and we pass this straight through here. Great, okay, that worked really well. So now we have a width of 100, let's change it to 200. Again, looks great.

So now we have a width of 100, let's change it to 200. Again, looks great. And we have our updated colour. And now let's try adding another colour picker, just to check that two aren't going to be clashing together. Let's change the default colour for this one. And let's maybe change the size so this one's a bit smaller. So we've got our colour1 and our colour2, our default for colour1 is red, our default for colour2 is green. We can set this one to blue, set this one to yellow.

for colour2 is green. We can set this one to blue, set this one to yellow. And they independently update of each other. And there are no issues with them conflicting at all. So this is definitely a successful integration. This JavaScript library appears to work very well with Filament, with Alpine AMP, and Livewire. This is a perfect candidate for a custom field.

Creating a custom form field class and view using the CLI. Consuming a custom field in a Filament form. Using an external JavaScript library in a custom field using Alpine.js. Using Livewire's "entangle" feature to bind an Alpine.js property to a Livewire property in the form. Defining configuration options for a custom field using getter and setter methods. Passing configuration options to an external JavaScript library.

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