---
sidebar_position: 6
title: Code du composant
---

Voici le code de notre composant principal, c'est lui qui va appeller notr item de base et réaliser une grande partie des actions.

## Création de la vue

```cshtml title="Components/Crafting.razor"
<CascadingValue Value="@this">
    <div class="container">
        <div class="row">
            <div class="col-6">
                
                <div>Available items:</div>
                <div>
                    <div class="css-grid">
                        
                        @foreach (var item in Items)
                        {
                            <CraftingItem Item="item" NoDrop="true"/>
                        }
                    </div>
                </div>

            </div>

            <div class="col-6">
                <div>Recipe</div>
                
                <div>
                    
                    <div class="css-recipe">
                        <CraftingItem Index="0"/>
                        <CraftingItem Index="1"/>
                        <CraftingItem Index="2"/>
                        <CraftingItem Index="3"/>
                        <CraftingItem Index="4"/>
                        <CraftingItem Index="5"/>
                        <CraftingItem Index="6"/>
                        <CraftingItem Index="7"/>
                        <CraftingItem Index="8"/>
                    </div>
                </div>
                
                <div>Result</div>
                <div>
                    <CraftingItem Item="RecipeResult"/>
                </div>
            </div>

            <div class="col-12">
                <div>Actions</div>
                <div class="actions" id="actions">

                </div>
            </div>
        </div>
    </div>
</CascadingValue>
```

## Création du code

Nous utilisons une `ObservableCollection` afin de permettre une action lors d'un changement dans la liste grâce à l'évenement `CollectionChanged`.

```csharp title="Components/Crafting.razor.cs"
public partial class Crafting
{
	private Item _recipeResult;

	public Crafting()
	{
		Actions = new ObservableCollection<CraftingAction>();
		Actions.CollectionChanged += OnActionsCollectionChanged;
		this.RecipeItems = new List<Item> { null, null, null, null, null, null, null, null, null };
	}

	public ObservableCollection<CraftingAction> Actions { get; set; }
	public Item CurrentDragItem { get; set; }

	[Parameter]
	public List<Item> Items { get; set; }

	public List<Item> RecipeItems { get; set; }

	public Item RecipeResult
	{
		get => this._recipeResult;
		set
		{
			if (this._recipeResult == value)
			{
				return;
			}

			this._recipeResult = value;
			this.StateHasChanged();
		}
	}

	[Parameter]
	public List<CraftingRecipe> Recipes { get; set; }

	/// <summary>
	/// Gets or sets the java script runtime.
	/// </summary>
	[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);
	}
}
```

Comme pour notre élément nous créons un fichier CSS pour notre composant.

```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;
}
```

Une classe JavaScript est aussi créée afin d'intéragir avec la 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);
        });
    }
}
```


## Notion : CascadingValue & CascadingParameter

Dans notre composant nous avons besoin de connaitre notre parent lors que nous nous trouvons dans un item afin d'apeller les méthodes `CheckRecipe()` ainsi que d'ajouter des actions ou de connaitre l'élément que nous déplaçons.

Nous utilisons donc dans la vue de notre composant `Crafting.razor` le code suivant :

```html title="Crafting.razor"
<CascadingValue Value="@this">
	...
		<CraftingItem Item="item" NoDrop="true"/>
	...
</CascadingValue>
```

Ce code permet de mettre à diposition notre composant dans l'ensemble des sous composant se trouvant dans la balise `CascadingValue`.

Pour récupérer notre `CascadingValue` il faut utiliser dans notre composant un `CascadingParameter` comme suite :

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

Il serait possible de passer par des `Parameter` mais en cas de multiple niveau, il faudrait dans chaque niveau ajouter notre `Parameter` pour les imbriquer :

```cshtml title="Components/MyRootComponent.razor"
@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public string Text { get; set; }
}

<div style="border: 1px solid black; padding: 10px;">
    <strong>MyRootComponent - @Text</strong>
    <div>
        @ChildContent
    </div>
</div>
```

```cshtml title="Components/MyFirstChildComponent.razor"
@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public MyRootComponent RootComponent { get; set; }
}

<div style="border: 1px solid black; padding: 10px;">
    <strong>MyFirstChildComponent - @RootComponent.Text</strong>
    <div>
        @ChildContent
    </div>
</div>
```

```cshtml title="Components/MySecondChildComponent.razor"
@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public MyRootComponent RootComponent { get; set; }
}

<div style="border: 1px solid black; padding: 10px;">
    <strong>MySecondChildComponent - @RootComponent.Text</strong>
    <div>
        @ChildContent
    </div>
</div>
```

```cshtml title="Pages/Index.razor"
@code
{
    MyRootComponent MyRootComponent;
}

<MyRootComponent @ref="MyRootComponent" Text="RootComponentText">
    <MyFirstChildComponent RootComponent="@MyRootComponent">
        <MySecondChildComponent RootComponent="@MyRootComponent">
            <div>MySecondChildComponent - Content</div>
        </MySecondChildComponent>
    </MyFirstChildComponent>
</MyRootComponent>
```

Cela oblige aussi le developpeur à déclarer à chaque utilisation la variable d'utilisation du composant ainsi que de devoir specifier un attribut avec sa valeur.

Alors qu'avec `CascadingValue` & `CascadingParameter` le code devient plus simple :

```cshtml title="Components/MyRootComponent.razor"
@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [Parameter]
    public string Text { get; set; }
}

<div style="border: 1px solid black; padding: 10px;">
    <strong>MyRootComponent - @Text</strong>
    <div>
        <CascadingValue Value="@this">
            @ChildContent
        </CascadingValue>
    </div>
</div>
```

```cshtml title="Components/MyFirstChildComponent.razor"
@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [CascadingParameter]
    public MyRootComponent RootComponent { get; set; }
}

<div style="border: 1px solid black; padding: 10px;">
    <strong>MyFirstChildComponent - @RootComponent.Text</strong>
    <div>
        @ChildContent
    </div>
</div>
```

```cshtml title="Components/MySecondChildComponent.razor"
@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    [CascadingParameter]
    public MyRootComponent RootComponent { get; set; }
}

<div style="border: 1px solid black; padding: 10px;">
    <strong>MySecondChildComponent - @RootComponent.Text</strong>
    <div>
        @ChildContent
    </div>
</div>
```

```cshtml title="Pages/Index.razor"
<MyRootComponent Text="RootComponentText">
    <MyFirstChildComponent>
        <MySecondChildComponent>
            <div>MySecondChildComponent - Content</div>
        </MySecondChildComponent>
    </MyFirstChildComponent>
</MyRootComponent>
```

## Notion : IJSRuntime

Pour appeler à JS partir de .net, injectez l' abstraction `IJSRuntime` et appelez l’une des méthodes suivantes :

* IJSRuntime.InvokeAsync
* JSRuntimeExtensions.InvokeAsync
* JSRuntimeExtensions.InvokeVoidAsync

Pour les méthodes .NET précédentes qui appellent des fonctions JS :

* L’identificateur de fonction ( `String` ) est relatif à la portée globale ( `window` ). Pour appeler `window.someScope.someFunction`, l’identificateur est `someScope.someFunction`. Il n’est pas nécessaire d’inscrire la fonction avant qu’elle ne soit appelée.
* Passe un nombre quelconque d'arguments on-Serializable JS dans `Object[]` à une fonction JS.
* Le jeton d’annulation ( `CancellationToken` ) propage une notification indiquant que les opérations doivent être annulées.
* `TimeSpan` représente une limite de temps pour une opération JS.
* Le type de retour `TValue` doit également être sérialisable. `TValue` doit correspondre au type .NET qui correspond le mieux au type sur retourné JS.
* `JS Un Promise` est retourné pour les méthodes `InvokeAsync`. `InvokeAsync` désencapsule `Promise` et retourne la valeur attendue par `Promise`.

Pour les applications pour lesquelles le Blazor Server prérendu est activé, l’appel de JS n’est pas possible pendant le prérendu initial. Les appels Interop JS doivent être différés jusqu’à ce que la connexion avec le navigateur soit établie. 

L’exemple suivant est basé sur `TextDecoder`, un décodeur basé sur JS. 
L’exemple montre comment appeler une fonction JS à partir d’une méthode C# qui décharge une spécification du code de développeur vers une API JS existante. 
La fonction JS accepte un tableau d’octets d’une méthode C#, décode le tableau et retourne le texte au composant pour l’affichage.

Ajoutez le code JS suivant à l’intérieur de la balise de fermeture `</body>` dans le fichier `wwwroot/index.html` :

```html
<script>
  window.convertArray = (win1251Array) => {
    var win1251decoder = new TextDecoder('windows-1251');
    var bytes = new Uint8Array(win1251Array);
    var decodedArray = win1251decoder.decode(bytes);
    console.log(decodedArray);
    return decodedArray;
  };
</script>
```

Le composant `CallJsExample1` suivant :

* Appelle la fonction JS `convertArray` avec `InvokeAsync` lors de la sélection d’un bouton ( `Convert Array` ).
* Une fois la fonction appelée JS, le tableau passé est converti en chaîne. La chaîne est retournée au composant pour l’affichage ( `text` ).

```cshtml title="Pages/CallJsExample1.razor"
@page "/call-js-example-1"
@inject IJSRuntime JS

<h1>Call JS <code>convertArray</code> Function</h1>

<p>
    <button @onclick="ConvertArray">Convert Array</button>
</p>

<p>
    @text
</p>

<p>
    <a href="https://www.imdb.com/title/tt0379786/" target="_blank">Serenity</a><br>
    <a href="https://www.imdb.com/name/nm0472710/" target="_blank">David Krumholtz on IMDB</a>
</p>

@code {
    private MarkupString text;

    private uint[] quoteArray = 
        new uint[]
        {
            60, 101, 109, 62, 67, 97, 110, 39, 116, 32, 115, 116, 111, 112, 32,
            116, 104, 101, 32, 115, 105, 103, 110, 97, 108, 44, 32, 77, 97,
            108, 46, 60, 47, 101, 109, 62, 32, 45, 32, 77, 114, 46, 32, 85, 110,
            105, 118, 101, 114, 115, 101, 10, 10,
        };

    private async Task ConvertArray()
    {
        text = new(await JS.InvokeAsync<string>("convertArray", quoteArray));
    }
}
```

### Appeler des fonctions JavaScript sans lire une valeur retournée ( `InvokeVoidAsync` )

Utiliser `InvokeVoidAsync` dans les cas suivants :

* .NET n’est pas nécessaire pour lire le résultat d’un appel JS.
* Les fonctions JS retournent `void (0)`/`void 0` ou non définie.

À l’intérieur de la balise de fermeture `</body>` de `wwwroot/index.html`, fournissez une fonction JS `displayTickerAlert1`. La fonction est appelée avec `InvokeVoidAsync` et ne retourne pas de valeur :

```html
<script>
  window.displayTickerAlert1 = (symbol, price) => {
    alert(`${symbol}: $${price}!`);
  };
</script>
```

`TickerChanged` appelle la méthode `handleTickerChanged1` dans le composant suivant `CallJsExample2`.

```cshtml
@page "/call-js-example-2"
@inject IJSRuntime JS

<h1>Call JS Example 2</h1>

<p>
    <button @onclick="SetStock">Set Stock</button>
</p>

@if (stockSymbol is not null)
{
    <p>@stockSymbol price: @price.ToString("c")</p>
}

@code {
    private Random r = new();
    private string? stockSymbol;
    private decimal price;

    private async Task SetStock()
    {
        stockSymbol = 
            $"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
        price = r.Next(1, 101);
        await JS.InvokeVoidAsync("displayTickerAlert1", stockSymbol, price);
    }
}
```

### Appeler des fonctions JavaScript et lire une valeur retournée ( `InvokeAsync` )

Utiliser `InvokeAsync` lorsque .net doit lire le résultat d’un appel JS.

À l’intérieur de la balise de fermeture `</body>` de `wwwroot/index.html`, fournissez une fonction JS `displayTickerAlert2`. L’exemple suivant retourne une chaîne à afficher par l’appelant :

```html
<script>
  window.displayTickerAlert2 = (symbol, price) => {
    if (price < 20) {
      alert(`${symbol}: $${price}!`);
      return "User alerted in the browser.";
    } else {
      return "User NOT alerted.";
    }
  };
</script>
```

`TickerChanged` appelle la méthode `handleTickerChanged2` et affiche la chaîne retournée dans le composant suivant `CallJsExample3`.

```cshtml title="Pages/CallJsExample3.razor"
@page "/call-js-example-3"
@inject IJSRuntime JS

<h1>Call JS Example 3</h1>

<p>
    <button @onclick="SetStock">Set Stock</button>
</p>

@if (stockSymbol is not null)
{
    <p>@stockSymbol price: @price.ToString("c")</p>
}

@if (result is not null)
{
    <p>@result</p>
}

@code {
    private Random r = new();
    private string? stockSymbol;
    private decimal price;
    private string? result;

    private async Task SetStock()
    {
        stockSymbol = 
            $"{(char)('A' + r.Next(0, 26))}{(char)('A' + r.Next(0, 26))}";
        price = r.Next(1, 101);
        var interopResult = 
            await JS.InvokeAsync<string>("displayTickerAlert2", stockSymbol, price);
        result = $"Result of TickerChanged call for {stockSymbol} at " +
            $"{price.ToString("c")}: {interopResult}";
    }
}
```
