---
sidebar_position: 6
title: Component code
---
Here is the code of our main component, it is he who will call our basic item and perform a large part of the actions.
## Creation of the view
```cshtml title="Components/Crafting.razor"
Available items:
@foreach (var item in Items)
{
}
Recipe
Result
Actions
```
## Code creation
We use an `ObservableCollection` to allow an action on a change in the list with the `CollectionChanged` event.
```csharp title="Components/Crafting.razor.cs"
public partial class Crafting
{
private Item _recipeResult;
public Crafting()
{
Actions = new ObservableCollection();
Actions.CollectionChanged += OnActionsCollectionChanged;
this.RecipeItems = new List { null, null, null, null, null, null, null, null, null };
}
public ObservableCollection Actions { get; set; }
public Item CurrentDragItem { get; set; }
[Parameter]
public List Items { get; set; }
public List RecipeItems { get; set; }
public Item RecipeResult
{
get => this._recipeResult;
set
{
if (this._recipeResult == value)
{
return;
}
this._recipeResult = value;
this.StateHasChanged();
}
}
[Parameter]
public List Recipes { get; set; }
///
/// Gets or sets the java script runtime.
///
[Inject]
internal IJSRuntime JavaScriptRuntime { get; set; }
public void CheckRecipe()
{
RecipeResult = null;
// Get the current model
var currentModel = string.Join("|", this.RecipeItems.Select(s => s != null ? s.Name : string.Empty));
this.Actions.Add(new CraftingAction { Action = $"Items : {currentModel}" });
foreach (var craftingRecipe in Recipes)
{
// Get the recipe model
var recipeModel = string.Join("|", craftingRecipe.Have.SelectMany(s => s));
this.Actions.Add(new CraftingAction { Action = $"Recipe model : {recipeModel}" });
if (currentModel == recipeModel)
{
RecipeResult = craftingRecipe.Give;
}
}
}
private void OnActionsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
JavaScriptRuntime.InvokeVoidAsync("Crafting.AddActions", e.NewItems);
}
}
```
As for our element we create a CSS file for our component.
```css title="Components/Crafting.razor.css"
.css-grid {
grid-template-columns: repeat(4,minmax(0,1fr));
gap: 10px;
display: grid;
width: 286px;
}
.css-recipe {
grid-template-columns: repeat(3,minmax(0,1fr));
gap: 10px;
display: grid;
width: 212px;
}
.actions {
border: 1px solid black;
height: 250px;
overflow: scroll;
}
```
A JavaScript class is also created to interact with the page.
```js title="Components/Crafting.razor.js"
window.Crafting =
{
AddActions: function (data) {
data.forEach(element => {
var div = document.createElement('div');
div.innerHTML = 'Action: ' + element.action + ' - Index: ' + element.index;
if (element.item) {
div.innerHTML += ' - Item Name: ' + element.item.name;
}
document.getElementById('actions').appendChild(div);
});
}
}
```
## Concept: CascadingValue & CascadingParameter
In our component we need to know our parent when we are in an item in order to call the `CheckRecipe()` methods as well as to add actions or to know the element we are moving.
We therefore use in the view of our component `Crafting.razor` the following code:
```html title="Crafting.razor"
...
...
```
This code makes it possible to make our component available in all the sub-components found in the `CascadingValue` tag.
To retrieve our `CascadingValue` we must use in our component a `CascadingParameter` as follows:
```csharp
[CascadingParameter]
public Crafting Parent { get; set; }
```
It would be possible to go through `Parameter` but in case of multiple levels, it would be necessary in each level to add our `Parameter` to nest them:
```cshtml title="Components/MyRootComponent.razor"
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public string Text { get; set; }
}
```
This also forces the developer to declare the component's usage variable each time it is used, as well as having to specify an attribute with its value.
Whereas with `CascadingValue` & `CascadingParameter` the code becomes simpler:
```cshtml title="Components/MyRootComponent.razor"
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public string Text { get; set; }
}
```
## Concept: IJSRuntime
To call JS from .net, inject the `IJSRuntime` abstraction and call one of the following methods:
* IJSRuntime.InvokeAsync
* JSRuntimeExtensions.InvokeAsync
* JSRuntimeExtensions.InvokeVoidAsync
For previous .NET methods that call JS functions:
* The function identifier ( `String` ) is relative to the global scope ( `window` ). To call `window.someScope.someFunction`, the identifier is `someScope.someFunction`. There is no need to register the function before it is called.
* Pass any number of on-Serializable JS arguments in `Object[]` to a JS function.
* The cancellation token ( `CancellationToken` ) propagates a notification that operations should be canceled.
* `TimeSpan` represents a time limit for a JS operation.
* The `TValue` return type must also be serializable. `TValue` should be the .NET type that best matches the JS returned type.
* `JS A Promise` is returned for `InvokeAsync` methods. `InvokeAsync` unwraps `Promise` and returns the value expected by `Promise`.
For apps that have Blazor Server pre-render enabled, invoking JS is not possible during the initial pre-render. Interop JS calls should be deferred until the connection with the browser is established.
The following example is based on `TextDecoder`, a JS-based decoder.
The sample demonstrates how to call a JS function from a C# method that offloads a specification from developer code to an existing JS API.
The JS function accepts a byte array from a C# method, decodes the array, and returns the text to the component for display.
Add the following JS code inside the closing `