Monthly Archives: August 2020

Resizing a component relative to it’s parent (position: fixed)

In the previous post I mentioned how we can fix a footer to the bottom of the viewport, but we have an issue where the width extends outside the width of the parent.

NOTE: This post discusses a partial solution to this issue. In that, if the browser window size changes this works, but if your viewport size changes, it doesn’t. So for example if you have a flyout Drawer component or the likes which takes up space on the page, then no window resize takes place, only a layout change takes place. If I resolve this I’ll update this post.

To solve this we’re going to create a ref on our parent div and monitor it’s size changes and then apply them to our footer.

So assuming we’re using material-ui’s createStyle, with the following CSS (or use plain CSS or your preferred CSS library)

footer: {
  position: "fixed",
  bottom: 0,
  marginBottom: "10px",
  textAlign: "left",
},

Here’s an abridged version of my code (using a React functional component)

const targetRef: any = useRef(null);
const [relativeWidth, setRelativeWidth] = useState(0);

useLayoutEffect(() => {
  function updateWidth() {
    if (targetRef != null && targetRef.current != null) {
      setRelativeWidth(targetRef.current.offsetWidth);
    }
  }

  window.addEventListener("resize", updateWidth);
  updateSize();
  return () => window.removeEventListener("resize", updateWidth);
}, []);

return (
  <div ref={targetRef}>
    {/* our other elements */}
    <div className={classes.footer}
      style={{ width: relativeWidth }}>
      {/* our footer elements */}
    </div>
  </div>
);

So what we’re doing is getting a ref to the parent div and storing the relativeWidth in the component’s state using useState (which we set up at the start of the function). Using useEffect, we create a listener to the window’s resize events and then update our state. Finally in the footer style we set the width explicitly using then relativeWidth that we stored.

Not only will this now display correctly relative to the parent div, but also resize relative to it as well.

These are early days for this code, but so far this looks to work a treat.

How to create a footer using CSS

To create a footer, a div that sits at the bottom of your viewport, using the position: “fixed” CSS.

For example here’s a snippet using material-ui’s createStyles function

footer: {
  position: "fixed",
  bottom: 0,
  marginBottom: "10px",
},

This will display the element it’s assigned to at the bottom of the screen using the fixed and bottom attributes, just to give it a bit of space we’ll using marginBottom so it floats above the bottom of the screen.

There’s a problem with the fixed position. It’s width is relative to the viewport, not relative to the parent.

This mean’s in a situation where you might have a user interface where the screen is divided into two columns and you want the fixed div in the right most column, you’ll find the width will extend outside if the view.

We’ll look at how to solve this, in the next post.

Accessing the host machine via the Android emulator

If you want to access a service/server running on the host machine you need to use specially designated IP addresses, such as 10.0.2.2.

Note: The following table is taken from Network address space

Network AddressDescription
10.0.2.1Router/gateway address
10.0.2.2Alias to your host loopback (i.e. 127.0.0.1 on the host machine)
10.0.2.3First DNS server
10.0.2.4/10.0.2.5/10.0.2.6Optional, second, third and fourth DNS server (if any)
10.0.2.15The emulated device network/ethernet interface
127.0.0.1The emulated device loopback interface

Blazor and the JavaScript interop.

In the post Logging in Blazor we saw how we can use the IJSRuntime to call out to the JavaScript runtime.

Ofcourse we can interop with any JavaScript code within our application, so for example let’s make some simple changes to the index.html from that post and add a script block, such as this

<script>
   function displayMessage(msg) {
      alert(msg);
   }
</script>

Nothing very special here, but we just want something visual to more easily see our interop calls.

Next, in our .razor file let’s simple add a button and associated code block

<button @onclick="DisplayMessage">Alert</button>

@code {
   private void DisplayMessage()
   {
      JsRuntime.InvokeVoidAsync("displayMessage", "Hello JavaScript World");
   }
}

The IJSRuntime interface has the following methods

public interface IJSRuntime
{
   ValueTask<TValue> InvokeAsync<TValue>(string identifier, 
      object[] args);
   ValueTask<TValue> InvokeAsync<TValue>(string identifier, 
      CancellationToken cancellationToken, object[] args);
}

As you can see, both return a ValueTask, hence are awaitable, but neither matches our InvokeVoidAsync method. This is because there are a bunch of extension methods within JSRuntimeExtensions. These extension methods check that the supplied IJSRuntime is not null and then calls the interface methods.

In the example above, we are not interested in the return value, so let’s now return some value from our JavaScript…

Change the script to

<script>
   function displayMessage(msg) {
      return prompt(msg);
   }
</script>

and now change the C# code in our code block to

private async void DisplayMessage()
{
   Debug.WriteLine(
      await JsRuntime.InvokeAsync<string>(
         "displayMessage", "Hello JavaScript World"
      )
   );
}

At this point, we’re receiving the return from our JavaScript function and (in the case of a WebAssembly application) outputting to the browser console.

Ofcourse we can now write JavaScript code and call this from Blazor as well.

Blazor/ASP.NET core on docker

I wanted to get a Blazor server application up and running on Ubuntu within a docker container (I’m running the whole thing on a Raspberry Pi 4 with Ubuntu Server).

The first stage for this post will simply be about creating a Dockerfile and creating a Blazor server application via the dotnet template.

We’re going to want the latest version of dotnet core, so let’s start by creating a very bare bones Dockerfile which will create an image based upon
mcr.microsoft.com/dotnet/core/sdk:3.1, it will also expose the standard HTTP port used in the Blazor server template, i.e. port 5000

Here’s the Dockerfile

FROM mcr.microsoft.com/dotnet/core/sdk:3.1
ENV APP_HOME /home
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME
EXPOSE 5000
CMD [ "bash" ]

To build this image run the following

docker rmi dotnet-env --force
docker build -t dotnet-env .

The first line is solely there to remove any existing image (which will be especially useful whilst developing the image). The second line will build the Dockerfile and name it dotnet-env.

Once built, let’s run the image to see all is good with it. So simply run

docker run -it --rm -p 5000:5000 -v /home/share:/home/share dotnet-env

In this example we’ll run docker in interactive mode and map ports using -p to map the host port 5000 to the exposed port in the image. We’ll also also created a volume link from the container to the host.

Once we’ve run the image up we should be placed into a BASH command prompt, now we can simply run

dotnet new blazorserver -o MyServer

To create the project MyServer, once created cd into the MyServer folder. Now run

dotnet run

A kestrel server should start up, and you might be able to access the server using http://server-ip-address. I say might, because you may well see an error at startup, saying something like

Unable to bind to http://localhost:5000 on the IPv6 loopback interface: ‘Cannot assign requested address’.

What you need to do is go into the Properties folder and open launchSettings.json, change the line

"applicationUrl": "https://localhost:5001;http://localhost:5000",

to

"applicationUrl": "http://0.0.0.0:5001;http://0.0.0.0:5000",

Next Step

This obvious next step to our docker build is to create a Docker image which contains our application and runs it when the container is started. We’re going to build and publish the using dotnet publish -c Release -o publish and then include the published files in our docker container, alternatively you might prefer to have the Dockerfile build and publish the project as part of its build process.

For now let’s just build our Blazor server application, then publish it to a folder.

We’re going to host the application in Kestrel, so before we go any further open the appsetting.json file from the publish folder and add the following

"Kestrel": {
  "EndPoints": {
    "Http": {
      "Url": "http://0.0.0.0:5000"
    }   
  }
},

Now we’ll make the changes to the Dockerfile to copy the published folder to the image and start up the Kestrel server when the image is run, here’s the Dockerfile

FROM mcr.microsoft.com/dotnet/core/sdk:3.1

ENV APP_HOME /home
RUN mkdir -p $APP_HOME

WORKDIR $APP_HOME

COPY ./BlazorServer/BlazorServer/publish ${APP_HOME}

EXPOSE 5000

CMD [ "dotnet", "BlazorServer.dll" ]

Now you should be able to access your server using http://your_server_name:5000.

Deploying my library to Github packages using Github actions

In my previous post I explained the steps to use Github actions to package and deploy your .nupkg to NuGet, but Github also includes support for you to deploy your package alongside your project sources.

If you take a look at the right hand of your project within Github (i.e. where you see your main README.md) you’ll notice the Packages section, if you click on the Publish your first package it tells you the steps you need to take to create and deploy your package, but it wasn’t quite that simple for me, hence this post will hopefully help others out a little if they hit similar issues.

The first thing you need to do is make sure you are using a version of the dotnet CLI that supports nuget commands (specifically the add command). Here’s a set-up step with a compatible version of dotnet CLI.

 - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1.401

The version must be 3.1.401 or above. There may be another before this but I was using 3.1.101 and was getting the following failure in the build output

Specify --help for a list of available options and commands.
error: Unrecognized command or argument 'add'

So the first command listed in Publish your first package will fail if you’re not upto date with the version of dotnet. If you’re using ubuntu instead of Windows for your builds, then you need to include the –store-password-in-clear-text on your dotnet nuget add command. Also change GH_TOKEN to ${{ secrets.GITHUB_TOKEN }}

Hence your first command will look more like this now

dotnet nuget add source https://nuget.pkg.github.com/your_user/index.json -n github -u your_user -p ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text

Replace “your_user” with your GitHub username and if you’ve created a permission that you prefer to use in place of GITHUB_TOKEN, then replace that also.

The second line shown in Publish your first package is

dotnet pack --configuration Release

Thankfully this is worked without an issue, however the Step 3 from didn’t work for me. It requires that I set the API key, similar to way we do this with NuGet publishing but using GITHUB_TOKEN again, hence the third step for me to publish to GitHub is

dotnet nuget push your_path/bin/Release/*.nupkg --skip-duplicate --api-key ${{secrets.GITHUB_TOKEN}} --source "github"

Replacing “your_path” with the path of your package to be published. Use –skip-duplicate so that any change to your code will not fail when a build is triggered, as without this option the command tries to publish an existing/unchanged package, causing a failure. Also set the –api-key as shown.

As I am already creating a package for NuGet and want to do the same for GitHub, we can condense the commands to something like

run: |
  dotnet nuget add source https://nuget.pkg.github.com/your_user/index.json -n github -u your_user -p ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text
  dotnet nuget push your_path/bin/Release/*.nupkg --skip-duplicate --api-key ${{secrets.GITHUB_TOKEN}} --source "github"

Don’t Forget

One more thing, in your nuspec or via the Properties | Package tab for your project, ensure you add a Repository URL to your source code. The Github package will fail without this.

For example in the SDK style csproj we have

<RepositoryUrl>https://github.com/putridparrot/PutridParrot.Randomizer.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>

The RepositoryType is not required for this to work, just using RepositoryUrl with https://github.com/putridparrot/PutridParrot.Randomizer worked fine.

Within a nuspec the format is as follows (if I recall)

<repository type="git" url="https://github.com/putridparrot/PutridParrot.Randomizer.git" />

Finally you’ll want to add https://nuget.pkg.github.com/your_user/index.json to you nuget.config to access Github’s NuGet package management from a project using this file and you will need a permission set up to access to API to pull in your package, alternatively simply download the package from Github and set-up a local nuget repository.

Deploying my library to NuGet using Github actions

I have a little library that I wanted to package up and make available on nuget.org. It’s nothing special, seriously it’s really not special (source available here PutridParrot.Randomizer).

Note: at the time of writing the library is very much in an alpha state, so don’t worry about the code etc., instead what we’re interested in is the .github/workflows/dotnet-core.yml.

Setting up NuGet

  • If you don’t already have a NuGet account then go to NuGet Register and sign up.
  • Next, click on the top right hand account name drop down. Clicking on this will display a drop down, from here select API Keys.
  • Click the Create option, and give your library a key name and ensure Push is checked and Push new packages and package version is selected, finally in the Glob Pattern just enter an *
  • Finally click the Create button to generate an API key

The API key should be kept secret as it will allow anyone who has access to it to upload package to your account. If for any reason you need to regenerate it, then from the Manage option on the API keys screen, click Regenerate.

Creating our deployment action

I’m using Github actions to build and then deploy my package. As you’ve seen, we need to use an API key to upload our package and obviously we do not want this visible in the build/deployment script, so the first thing we need to do is create a Github secret.

  • Go to the Settings for your project on Github
  • Select the Secrets tab on the left of the screen
  • Click New Secret
  • Give your secret a name, i.e. NUGET_API_KEY
  • In the value, of the secret, place the API key you got from NuGet

This secret is then accessible from your Github action scripts.

Note: You might be thinking, “didn’t this post say the key was to be kept secret”, I’m assuming as Microsoft owns Github and NuGet that we’re fairly safe letting them have access to the key.

Now we need to add a step to package and then a step to deploy to nuget to our Github workflow/actions.

We’re not going to be doing anything clever with our package, so not creating a nuspec for the project or signing it (at this time). So we’ll just use the dotnet CLI pack option. Here’s the addition to these dotnet-core.yml workflow for step

- name: Create Package
  run: dotnet pack --configuration Release
- name: Publish to Nuget
  run: dotnet nuget push /home/runner/work/PutridParrot.Randomizer/PutridParrot.Randomizer/PutridParrot.Randomizer/bin/Release/*.nupkg --skip-duplicate --api-key ${{secrets.NUGET_API_KEY}} --source https://api.nuget.org/v3/index.json

In the above, we have a step “Create Package” to create our package using the dotnet CLI’s pack command using release configuration. If (as I am) you’re using an ubuntu image to run your workflow this will be stored a a folder similar to this one listed below

/home/runner/work/PutridParrot.Randomizer/PutridParrot.Randomizer/PutridParrot.Randomizer/bin/Release/

Where you replace PutridParrot.Randomizer with you project name. Or better still, check the build logs to see where the output should show where the .nupkg was written to.

As you can see from the “Publish to Nuget” step, you’ll also need to reuse the path there where we again use the dotnet cli to push our nupk file to NuGet using the secret we defined earlier as the API key. In this usage we’re also not pushing if the package is a duplicate.

That’s it.

_Note: One side note – if you go onto nuget.org to check your packages, they may by in “Unlisted” state initially. This should change automatically after a few minutes to Published._

Mounting a USB HDD on Ubuntu server

I’m running up my latest Raspberry Pi with a connected USB SSD and forgot how to mount the thing. So this post is a little reminder. Ofcourse the instruction are not exclusive to Raspberry Pi’s or Ubuntu, but hey I just wanted a back story to this post.

So you’ve connected your USB drive and you’ve got a SSH terminal (i.e. via PuTTY) session connected to you Ubuntu server, what’s next?

Where’s my drive

The first thing you need to figure out is where your drive is, i.e. what /dev/ location it’s assigned to.

  • Try running df, which reports disk space and usage, from this you might be able to spot your drive, in my case it’s /dev/sda1 and its obvious by the size of the drive
  • If this is not conclusive then run sudo fdisk -l this may help locate the disk

Mounting the drive

Assuming we’ve located the device, and for this example it is on /dev/sda1, how do we mount this drive?

  • Create a folder that will get mapped to our mounted drive, for example mkdir /media/external or whatever you want to name it
  • To mount the drive we now use
    sudo mount -t ntfs-3g /dev/sda1 /media/external
    

    Obviously this drive I’m mounting is an NTFS formatted drive, you might be using vfat or something else instead, so check. Then we simply have the source (/dev/sda1) mapping to the destination (/media/external) that we created.

    Note: without the mount taking place, ls the destination and you’ll see no files (if they exist on your drive). After you mount the drive. ls the destination to see the files on the USB drive.

As it currently stands, when you reboot you’ll have to mount the drive again.

Auto-mounting the drive

In scenarios where we’re not removing the drive and simply want the OS to automount our drive, we need to do a couple of things.

  • Find the UUID for the drive by running sudo blkid, next to the device, i.e. /dev/sda1 should be a UUID, copy or note down the string.
  • Run sudo nano fstab
  • Add a new line to the bottom of the file along these lines
    UUID=ABCDEFGHIJK /media/external auto nosiud,nodev,nofail 0 0
    

    and save the file

  • Run sudo mount -a to check for errors

Now when you reboot the machine the USB drive should be mounted automatically.