DMIT-2018 / dmit-2018-jan-2023-a02-workbook-jthompsonnait

dmit-2018-jan-2023-a02-workbook-jthompsonnait created by GitHub Classroom
1 stars 0 forks source link

Gotcha with Blazor #6

Open jthompsonnait opened 1 year ago

jthompsonnait commented 1 year ago

Two-Way Binding with Input elements

<label>Order No:</label>
<input type="number" @bind-value="OrderId" />
[Parameter]
public int OrderId { get; set; }

Two-Way Binding with MatTextField

<label>Search Pattern</label>
<MatTextField @bind-Value="@searchPattern"/>
[Parameter]
private string searchPattern { get; set; }

Methods will auto-fire if you include the parameters brackets

Bad Code

<button class="btn-success" onclick="@Save()">Save</button>

Good Code

<button class="btn-success" onclick="@Save">Save</button>
jthompsonnait commented 1 year ago

Setup

Paginator/Table Template

BlazorPagination

Paginator


Mat Blazor Setup

_Import.razor

@using MatBlazor

_Host.cshtml

<script src="_content/MatBlazor/dist/matBlazor.js"></script>
<link href="_content/MatBlazor/dist/matBlazor.css" rel="stylesheet" />
<component type="typeof(App)" render-mode="Server" />

Program.cs

using MatBlazor;
builder.Services.AddMatBlazor();

jthompsonnait commented 1 year ago

LINQ & LINQ Methods

Rules for moving from LINQ Pad methods to Visual Studio methods

// this now becomes the following trackExist = _playlistManagementContext.Tracks .Where(x => x.TrackId == trackId) ....

// saving data to the database now becomes the following _playlistManagementContext.SaveChanges();


## Error Handling

- In LINQ Pad, when you run a method and an error(s) is thrown from the error list, the process terminates after showing the error message(s).  However, on a web server, after the errors(s) are thrown, the LINQ transactions is still held in memory with the assumption that we are going to handle it.  If we try and do any further LINQ transactions, we will get the following message:
<br>

_The instance of entity type 'PlaylistTrack' cannot be tracked because another instance with the same key value for {'PlaylistId', 'TrackId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values._

<br>
Because of this, we need to clear the tracking of any notable changes the system is referencing.

```csharp
//  we need to clear the "track changes"; otherwise, we leave our entity system in flux
  _playlistManagementContext.ChangeTracker.Clear();
jthompsonnait commented 1 year ago

Blazor Route Parameters

Navigation From

NavigationFrom.razor

@page "/SamplePages/NavigationFrom"
<h3>Navigation From</h3>

<TableTemplate Items="PaginatorTrackSelection.Results" Context="TrackSelectionView">
    <TableHeader>
        <th></th>
        <th class="SortField @GetSortColumn("SongName")" @onclick="@( () => Sort("SongName"))">Song</th>
        <th class="SortField @GetSortColumn("AlbumTitle")" @onclick="@( () => Sort("AlbumTitle"))">Album</th>
        <th class="SortField @GetSortColumn("ArtistName")" @onclick="@( () => Sort("ArtistName"))">Artist</th>
        <th class="SortField @GetSortColumn("Milliseconds")" @onclick="@( () => Sort("Milliseconds"))">Length</th>
        <th>Price</th>
    </TableHeader>
    <RowTemplate>
        <td>
            <button class="btn-success" @onclick="(() =>  Edit(TrackSelectionView.TrackId))">Edit</button>
        </td>
        <td>@TrackSelectionView.SongName</td>
        <td>@TrackSelectionView.AlbumTitle</td>
        <td>@TrackSelectionView.ArtistName</td>
        <td>@TrackSelectionView.Length</td>
        <td>@TrackSelectionView.Price</td>
    </RowTemplate>
</TableTemplate>
<div align="center">
    <BlazorPager CurrentPage="PaginatorTrackSelection.CurrentPage"
                 PageCount="PaginatorTrackSelection.PageCount"
                 VisiblePages="3"
                 ShowFirstLast="true"
                 ShowPageNumbers="true"
                 FirstText="&laquo;"
                 LastText="&raquo;"
                 NextText="&rsaquo;"
                 PreviousText="&lsaquo;"
                 OnPageChanged="(async e => { CurrentPage = e; await FetchArtistOrAlbumTracks();})">
    </BlazorPager>
</div>

NavigationFrom.razor.cs

public partial class NavigationFrom
    {
        [Inject]
        protected PlaylistTrackService? PlaylistTrackService { get; set; }
        [Inject]
        protected NavigationManager? NavigationManager { get; set; }

        #region Fields
        private string searchPattern { get; set; } = "Deep";
        private string searchType { get; set; } = "Artist";
        #endregion

        #region Paginator
        private const int PAGE_SIZE = 10;
        protected string SortField { get; set; } = "Owner";
        protected string Direction { get; set; } = "desc";
        protected int CurrentPage { get; set; } = 1;
        protected PagedResult<TrackSelectionView> PaginatorTrackSelection { get; set; } = new();

        private async void Sort(string column)
        {
            Direction = SortField == column ? Direction == "asc" ? "desc" : "asc" : "asc";
            SortField = column;
            if (!string.IsNullOrWhiteSpace(searchPattern))
            {
                await FetchArtistOrAlbumTracks();
            }
        }

        private string GetSortColumn(string x)
        {
            return x == SortField ? Direction == "desc" ? "desc" : "asc" : "";
        }
        #endregion

        protected override async Task OnInitializedAsync()
        {
            await base.OnInitializedAsync();
            //  load data on page load
            await FetchArtistOrAlbumTracks();
            await InvokeAsync(StateHasChanged);
        }

        private async Task FetchArtistOrAlbumTracks()
        {
            PaginatorTrackSelection = await PlaylistTrackService.FetchArtistOrAlbumTracks(searchType, searchPattern, CurrentPage, PAGE_SIZE, SortField, Direction);        //Note that the following line is necessary because otherwise
            await InvokeAsync(StateHasChanged);
        }

        //  pass the track Id to the "NavigationTo" page
        protected void Edit(int trackId)
        {
            NavigationManager.NavigateTo($"/SamplePages/NavigationTo/{trackId}");
        }
    }

Navigation To

NavigationTo.razor

@page "/SamplePages/NavigationTo/{EditTrackId}"
<h3>Navigation To</h3>
<div>
    <label>Track ID:</label>
    <label>@trackSelection.TrackId</label>
    <p>
        <label>Album Title:</label>
        <label>@trackSelection.AlbumTitle</label>
    </p>
    <p>
    <label>Artist Name:</label>
    <label>@trackSelection.ArtistName</label>
    </p>
    <p>
        <label>Song Name:</label>
        <label>@trackSelection.SongName</label>
    </p>
</div>

NavigationTo.razor.cs

public partial class NavigationTo
    {
        [Inject] protected PlaylistTrackService? PlaylistTrackService { get; set; }

        #region Fields

        private TrackSelectionView trackSelection { get; set; } = new();
        #endregion

        #region Properties
        [Parameter]
        public string EditTrackId { get; set; }
        #endregion

        protected override async Task OnInitializedAsync()
        {
            await base.OnInitializedAsync();
            EditTrackId = EditTrackId;
            //  check to see if we are navigating using a valid track Id
            //      or are we going directly to the page from the menu
            if (!string.IsNullOrWhiteSpace(EditTrackId))
            {
                trackSelection = await PlaylistTrackService.GetTrack(int.Parse(EditTrackId));
            }
            await InvokeAsync(StateHasChanged);
        }
    }

NavMenu.razor

<NavLink class="nav-link" href="/SamplePages/NavigationFrom">
    <span>Navigation From</span>
</NavLink>
<NavLink class="nav-link" href="/SamplePages/NavigationTo">
    <span>Navigation To</span>
</NavLink>
jthompsonnait commented 1 year ago

Displaying Successful Feedback & Error Messages on a Web Page

When displaying an AggregateException within a LINQ Pad editor, you would run the following code to show the exception.

catch (AggregateException ex)
{
    foreach (var error in ex.InnerExceptions)
    {
        error.Message.Dump();
    }
}

To display the output on our HTML web page, we will code the following:

Fields for holding our messages and error lists

// placeholder for feedback messages
private string feedBackMessage { get; set; }

// placeholder for error messages
private string errorMessage { get; set; }

//a get property that returns the result of the lamda action
private bool hasError => !string.IsNullOrWhiteSpace(errorMessage);
private bool hasFeedBack => !string.IsNullOrWhiteSpace(feedBackMessage);

//used to display any collection of errors on web page
//whether the errors are generated locally OR come form the class library
//      service methods
private List<string> errorDetails { get; set; } = new();

Try/Catch Method

try
{
    //  reset the error detail list
    errorDetails.Clear();

    //  reset error message to an empty string
    errorMessage = string.Empty;

    //  reset feedback message to an empty string
    feedBackMessage = string.Empty;

    //  call our service methods from the BLL
    Service.ServiceMethod();

    //  update the feedback with a successful message
    feedBackMessage = "XXX was successfully XXXX";
}
catch (ArgumentNullException ex)
{
    errorMessage = GetInnerException(ex).Message;
}
catch (ArgumentException ex)
{

    errorMessage = GetInnerException(ex).Message;
}
catch (AggregateException ex)
{
    //  having collected a number of errors
    //  each error should be places into a separate line
    errorMessage = "Unable to add a xxxxx";
    foreach (var error in ex.InnerExceptions)
    {
        errorDetails.Add(error.Message);
    }
}
catch (Exception ex)
{
    errorMessage = GetInnerException(ex).Message;
}

HTML Code

@if (@hasFeedBack)
{
    <blockquote class="success">@feedBackMessage</blockquote>
}

@if (hasError)
{
    <blockquote class="error">
        @errorMessage
        <ul>
            @foreach (var detail in errorDetails)
            {
                <li>@detail</li>
            }
        </ul>
    </blockquote>
}