---
sidebar_position: 5
title: Component subcomponent
---

Our component will mainly call for an item, whether to fill the grid, display the boxes of the recipe and even the result.

In order not to have to repeat code in our component, we will therefore create a sub-component which will allow the display of our elements.

Our component remains simple but uses the Drag & Drop method.

## Creating our base component view

```cshtml title="Components/CraftingItem.razor"
<div
    class="item"
    ondragover="event.preventDefault();"
    draggable="true"
    @ondragstart="@OnDragStart"
    @ondrop="@OnDrop"
    @ondragenter="@OnDragEnter"
    @ondragleave="@OnDragLeave">

    @if (Item != null)
    {
        @Item.DisplayName
    }
</div>
```

We use Razor's envent handling for Drag & Drop methods.

## Creating our component code

The code of our item remains simple, we call on our parent in order to pass it the action information as well as Drag & Drop.

```csharp title="Components/CraftingItem.razor.cs"
public partial class CraftingItem
{
	[Parameter]
	public int Index { get; set; }

	[Parameter]
	public Item Item { get; set; }

	[Parameter]
	public bool NoDrop { get; set; }

	[CascadingParameter]
	public Crafting Parent { get; set; }

	internal void OnDragEnter()
	{
		if (NoDrop)
		{
			return;
		}

		Parent.Actions.Add(new CraftingAction { Action = "Drag Enter", Item = this.Item, Index = this.Index });
	}

	internal void OnDragLeave()
	{
		if (NoDrop)
		{
			return;
		}

		Parent.Actions.Add(new CraftingAction { Action = "Drag Leave", Item = this.Item, Index = this.Index });
	}

	internal void OnDrop()
	{
		if (NoDrop)
		{
			return;
		}

		this.Item = Parent.CurrentDragItem;
		Parent.RecipeItems[this.Index] = this.Item;

		Parent.Actions.Add(new CraftingAction { Action = "Drop", Item = this.Item, Index = this.Index });

		// Check recipe
		Parent.CheckRecipe();
	}

	private void OnDragStart()
	{
		Parent.CurrentDragItem = this.Item;

		Parent.Actions.Add(new CraftingAction { Action = "Drag Start", Item = this.Item, Index = this.Index });
	}
}
```

It is also possible to create CSS style files directly for our component, this file will be automatically rendered with our component.

```css title="Components/CraftingItem.razor.css"
.item {
    width: 64px;
    height: 64px;
    border: 1px solid;
    overflow: hidden;
}
```

## Concept: Event handling

Specify delegate event handlers in Razor component markup with `@on{DOM EVENT}="{DELEGATE}"` Razor syntax:
* The `{DOM EVENT}` placeholder is a Document Object Model (DOM) event (for example, `click`).
* The `{DELEGATE}` placeholder is the C# delegate event handler.

For event handling:
* Asynchronous delegate event handlers that return a Task are supported.
* Delegate event handlers automatically trigger a UI render, so there's no need to manually call StateHasChanged.
* Exceptions are logged.

The following code:
* Calls the `UpdateHeading` method when the button is selected in the UI.
* Calls the `CheckChanged` method when the checkbox is changed in the UI.

```cshtml title="Pages/EventHandlerExample1.razor"
@page "/event-handler-example-1"

<h1>@currentHeading</h1>

<p>
    <label>
        New title
        <input @bind="newHeading" />
    </label>
// highlight-next-line
		Update heading
    </button>
</p>

<p>
    <label>
// highlight-next-line
        <input type="checkbox" @onchange="CheckChanged" />
        @checkedMessage
    </label>
</p>

@code {
    private string currentHeading = "Initial heading";
    private string? newHeading;
    private string checkedMessage = "Not changed yet";
	
// highlight-start
    private void UpdateHeading()
    {
        currentHeading = $"{newHeading}!!!";
    }

    private void CheckChanged()
    {
        checkedMessage = $"Last changed at {DateTime.Now}";
    }
// highlight-end
}
```

In the following example, `UpdateHeading`:
* Is called asynchronously when the button is selected.
* Waits two seconds before updating the heading.

```cshtml title="Pages/EventHandlerExample2.razor"
@page "/event-handler-example-2"

<h1>@currentHeading</h1>

<p>
    <label>
        New title
        <input @bind="newHeading" />
    </label>
// highlight-next-line
    <button @onclick="UpdateHeading">
        Update heading
    </button>
</p>

@code {
    private string currentHeading = "Initial heading";
    private string? newHeading;

// highlight-start
    private async Task UpdateHeading()
    {
        await Task.Delay(2000);

        currentHeading = $"{newHeading}!!!";
    }
// highlight-end
}
```

### Event arguments

For events that support an event argument type, specifying an event parameter in the event method definition is only necessary if the event type is used in the method. 

In the following example, `MouseEventArgs` is used in the `ReportPointerLocation` method to set message text that reports the mouse coordinates when the user selects a button in the UI.

```cshtml title="Pages/EventHandlerExample3.razor"
@page "/event-handler-example-3"

@for (var i = 0; i < 4; i++)
{
    <p>
        <button @onclick="ReportPointerLocation">
            Where's my mouse pointer for this button?
        </button>
    </p>
}

<p>@mousePointerMessage</p>

@code {
    private string? mousePointerMessage;

// highlight-start
    private void ReportPointerLocation(MouseEventArgs e)
    {
        mousePointerMessage = $"Mouse coordinates: {e.ScreenX}:{e.ScreenY}";
    }
// highlight-end
}
```

### Supported EventArgs

| Event | Class | Document Object Model (DOM) events |
| ---- | ---- | ---- |
| Clipboard | ClipboardEventArgs | `oncut`, `oncopy`, `onpaste` |
| Drag | DragEventArgs | `ondrag`, `ondragstart`, `ondragenter`, `ondragleave`, `ondragover`, `ondrop`, `ondragend` |
| Error | ErrorEventArgs | `onerror` |
| General | EventArgs | `onactivate`, `onbeforeactivate`, `onbeforedeactivate`, `ondeactivate`, `onfullscreenchange`, `onfullscreenerror`, `onloadeddata`, `onloadedmetadata`, `onpointerlockchange`, `onpointerlockerror`, `onreadystatechange`, `onscroll` |
| Clipboard | EventArgs | `onbeforecut`, `onbeforecopy`, `onbeforepaste` |
| Input | EventArgs | `oninvalid`, `onreset`, `onselect`, `onselectionchange`, `onselectstart`, `onsubmit` |
| Media | EventArgs | `oncanplay`, `oncanplaythrough`, `oncuechange`, `ondurationchange`, `onemptied`, `onended`, `onpause`, `onplay`, `onplaying`, `onratechange`, `onseeked`, `onseeking`, `onstalled`, `onstop`, `onsuspend`, `ontimeupdate`, `ontoggle`, `onvolumechange`, `onwaiting` |
| Focus | FocusEventArgs | `onfocus`, `onblur`, `onfocusin`, `onfocusout` |
| Input | ChangeEventArgs | `onchange`, `oninput` |
| Keyboard | KeyboardEventArgs | `onkeydown`, `onkeypress`, `onkeyup` |
| Mouse | MouseEventArgs | `onclick`, `oncontextmenu`, `ondblclick`, `onmousedown`, `onmouseup`, `onmouseover`, `onmousemove`, `onmouseout` |
| Mouse pointer | PointerEventArgs | `onpointerdown`, `onpointerup`, `onpointercancel`, `onpointermove`, `onpointerover`, `onpointerout`, `onpointerenter`, `onpointerleave`, `ongotpointercapture`, `onlostpointercapture` |
| Mouse wheel | WheelEventArgs | `onwheel`, `onmousewheel` |
| Progress | ProgressEventArgs | `onabort`, `onload`, `onloadend`, `onloadstart`, `onprogress`, `ontimeout` |
| Touch | TouchEventArgs | `ontouchstart`, `ontouchend`, `ontouchmove`, `ontouchenter`, `ontouchleave`, `ontouchcancel` |
