Creating web apps in PowerShell with Pglet
You can use PowerShell to build standalone web apps or add web UI to existing scripts.
Install pglet
module​
System requirements:
- Windows PowerShell 5.1
- PowerShell Core 7 or above on Windows, Linux or macOS
To install pglet
module run the following command in PowerShell session:
Install-Module pglet
Create your first app​
Create a new PowerShell script app.ps1
with the following contents:
Import-Module pglet
$page = Connect-PgletPage
$page.Add((Text -Value "Hello, world!"))
$page.Close()
When you run the script a new browser window will popup with "Hello, world!" text:
Connect-PgletPage
connects to a local instance of Pglet Server and creates a new page with a random URL. You can specify page name, so it has a permanent URL:
$page = Connect-PgletPage -Name "my-app"
The line with $page.Add
adds Text
control to a page's Controls
collection and sends page update to Pglet. $page.Add()
is a shortcut for:
$page.Controls.Add((Text -Value "Hello, world!"))
$page.Update()
It is important to call $page.Close()
at the end of your script to close WebSocket connection to Pglet server. We recommend using try...finally
pattern for guaranteed cleanup as finally
block is called whenever you break the execution of the script with CTRL+C
:
Import-Module pglet
$page = Connect-PgletPage -Name "my-app"
try {
# your code here
} finally {
$page.Close()
}
note
If you run your script with pwsh app.ps1
you can ommit $page.Close()
as all connections are automatically closed on PowerShell session end.
Now, try running app.ps1
a few more times. You'll notice that a new "Hello, world!" text is added at the end of the page every time you run the script. This is because the contents of a page is persistent. You can clean the page at beginning of your script with $page.Clean()
:
Import-Module pglet
$page = Connect-PgletPage -Name "my-app"
try {
$page.Clean()
$page.Add((Text -Value (Get-Date)))
} finally {
$page.Close()
}
Now, every run of the script above will replace page contents with the current date/time.
note
You can disable automatic browser opening with -NoWindow
parameter:
$page = Connect-PgletPage -Name "my-app" -NoWindow
Multiple pages can be updated from the same script:
Import-Module pglet
$page1 = Connect-PgletPage -Name "page-1"
$page2 = Connect-PgletPage -Name "page-2"
try {
$page1.Clean()
$page1.Add((Text -Value "Hello, page 1!"))
$page2.Clean()
$page2.Add((Text -Value "Hello, page 2!"))
} finally {
$page1.Close()
$page2.Close()
}
note
All code examples below will be assuming that you put them into try...finally
wrapper:
Import-Module pglet
$page = Connect-PgletPage -Name "myapp"
try {
$page.Clean()
# Insert example code here
} finally {
$page.Close()
}
Displaying data​
Text​
Text
control is used to output textual data. Its main properties are Value
and Size
, but it also has a number of formatting properties to control its appearence. For example:
Text -Value 'Centered Text' -Size xlarge -Align Center -VerticalAlign Center -Width 100 -Height 100 `
-Color 'White' -BgColor 'Salmon' -Padding 5 -Border '1px solid #555'
You create control with Text
cmdlet, add it to Controls
collection of $page
(or children collection of other container control such as Stack
) and then call $page.Update()
to send local page changes to Pglet server:
$txt = Text -Value "Hi there!"
$page.Controls.Add($txt)
$page.Update()
You can update control properties and push the changes again:
$txt.Text = "Current date is: $(GetDate)"
$txt.Color = "Blue"
$page.Update()
You can even do some animations, for example:
$text = Text -Value 'Centered Text' -Size xlarge -Align Center -VerticalAlign Center -Width 100 -Height 100 `
-Color 'White' -BgColor 'Salmon' -Padding 5 -Border '1px solid #555'
$page.Add($text)
for($i = 0; $i -le 50; $i++) {
$text.Value = "Radius $i"
$text.BorderRadius = $i
$page.Update()
Start-Sleep -Milliseconds 50
}
$page.Update()
is smart enough to send only the changes made since its last call, so you can add a few new controls to the page, remove some of them, change control properties and then call $page.Update()
to do batched update, for example:
for($i = 0; $i -le 20; $i++) {
$page.Controls.Add((Text "Line $i"))
if ($i -gt 4) {
$page.Controls.RemoveAt(0)
}
$page.Update()
Start-Sleep -Milliseconds 300
}
Markdown​
Text
control is able to display markdown rich contents if -Markdown
parameter is added/enabled, for example:
$t = Text -Markdown -Value '# Using Markdown with Pglet
You can add `-Markdown` parameter to `Text` cmdlet
to output **rich** *text*.
[GitHaub Flavored Markdown](https://github.github.com/gfm/) is supported.
This is a code snippet:
```
import Pglet
```
'
$page.Add($t)
HTML​
You can use Html
control to add raw HTML to the page if absolutely required:
$html = Html -Value '<h1>Hello, world!</h1>
<p>This is a test paragraph with a <a href="https://pglet.io">link</a>.</p>'
$page.Add($html)
Progress​
Use Progress
control to display a progress bar. For example, to display a progress of imaginary copy operation:
$prog1 = Progress -Label "Copying /file1.txt to /file2.txt" -Width "30%" -BarHeight 4
$page.Add($prog1)
for($i = 0; $i -le 100; $i=$i+5) {
$prog1.Value = $i
$prog1.Update()
Start-Sleep -Milliseconds 100
}
You can use Description
property to display the progress of some multi-step operation:
$prog2 = Progress -Label "Create new account" -Width "30%"
$page.Add($prog2)
$steps = @('Preparing environment...', 'Collecting information...', 'Performing operation...', 'Complete!')
for($i = 0; $i -lt $steps.Length; $i++) {
$prog2.Description = $steps[$i]
$prog2.Value = 100 / ($steps.Length - 1) * $i
$page.Update()
Start-Sleep -Seconds 1
}
Spinner​
Use Spinner
control to visualize an indeterminate progress:
$sp = Spinner -Label "Please wait while the process is running..." -LabelPosition Right
$page.Add($sp)
Getting user input​
Making interactive web apps with Pglet is a breeze! It's not just limited to displaying data, but you can request an input from a user and respond to various events generated by page controls.
Button​
Button
is the most essential input control which generates click
event when pressed:
$btn = Button "Click me!"
$page.Add($btn)
[SCREENSHOT?]
All events generated by controls on a web page are continuously sent back to your script, so how do you respond to a button click?
Waiting for events​
Linear program:
- Wait for input.
- Process data.
- Display results.
- Rinse, repeat.
Waiting for the next event with Wait-PgletEvent
:
$btn = Button -Text "Click me!"
$page.Add($btn)
Wait-PgletEvent
Output:
Control : Pglet.PowerShell.Controls.PsButton
Page : Pglet.PowerShell.Controls.PsPage
Target : _30
Name : click
Data :
TBD - describe event object
Event handlers​
"Counter" app with Switch-PgletEvents
:
Import-Module pglet
$page = Connect-PgletPage -Name "counter"
try {
$page.Clean()
$num_txt = TextBox -Value 0
$minus_btn = Button "-" -OnClick {
$num_txt.Value = [int]$num_txt.Value - 1
$page.Update()
}
$plus_btn = Button "+" -OnClick {
$num_txt.Value = [int]$num_txt.Value + 1
$page.Update()
}
$page.Add((Stack -Horizontal -Controls @(
$minus_btn
$num_txt
$plus_btn
)))
Switch-PgletEvents
}
finally {
$page.Close()
}
Textbox​
Pglet provides a number of controls for building forms: Textbox, Checkbox, Dropdown, Button.
Let's ask a user for a name:
Import-Module pglet
Connect-PgletPage "greeter"
Invoke-Pglet "clean"
Invoke-Pglet "add textbox label='Your name' description='Please provide your full name'"
Invoke-Pglet "add button primary text='Say hello'"
Checkbox​
TBD
Dropdown​
TBD
Grid​
Grid with auto-generated columns displaying a list of Hashtable
objects:
$items = @(
@{
"First name" = "John"
"Last name" = "Smith"
}
@{
"First name" = "Alice"
"Last name" = "Brown"
}
)
$grid = Grid -Items $items
$page.Add($grid)
Grid with auto-generated columns displaying the results (PSObject[]
) of PowerShell command:
$items = Get-Command
$grid = Grid -Items $items
$page.Add($grid)
Grid with explicitly defined columns displaying a list of custom class instances:
class Person {
[string]$FirstName
[string]$LastName
Person($firstName, $lastName) {
$this.FirstName = $firstName
$this.LastName = $lastName
}
}
$items = @(
[Person]::new('John', 'Smith')
[Person]::new('Alice', 'Brown')
)
$grid = Grid -Items $items -Columns @(
GridColumn -Name "First name" -FieldName "FirstName" -Sortable "string"
GridColumn -Name "Last name" -FieldName "LastName" -Sortable "string"
)
$page.Add($grid)
Charts​
TBD
Layout​
Stack...
Multi-host dashboards​
TBD
Multi-user apps​
In multi-user Pglet apps every user has a unique session with its own page contents. To start an app page you use Connect-PgletApp
cmdlet which takes a ScriptBlock
with a session handler code. The handler script is called in a separate PowerShell Runspace for every new user connected. The program stays blocked on Connect-PgletApp
while constantly waiting for new user connections.
Below is a minimal Pglet multi-user app in PowerShell:
Import-Module pglet
Connect-PgletApp -Name 'greeter-app' -ScriptBlock {
Invoke-Pglet "add text value='Hello to connection $PGLET_CONNECTION_ID!'"
}
Now, a multi-user version of greeter app could look like the following:
Import-Module pglet
Connect-PgletApp -Name 'greeter-app' -ScriptBlock {
$txt_name = Invoke-Pglet "add textbox label='Your name' description='Please provide your full name'"
$btn_hello = Invoke-Pglet "add button primary text='Say hello'"
while($true) {
$e = Wait-PgletEvent
if ($e.Target -eq $btn_hello -and $e.Name -eq 'click') {
$name = Invoke-Pglet "get $txt_name value"
Invoke-Pglet "clean page"
Invoke-Pglet "add text value='Hello, $name!'"
return
}
}
}
Publishing app​
Up until this moment you've been running all tutotial samples on your computer with a local Pglet server instance running in the background.
With literarily no changes to the code Pglet allows to make your program accessible from the web. This could be an admin app for managing backend services, or a dashboard with server metrics, or an application prototype you are sharing with your colleagues or clients.
In contrast to a classic deployment you are not packaging your program and it's not going anywhere. It continues to run on the same computer where it was built or cloned while UI is "streamed" to Pglet service and available via https://app.pglet.io/public/{your-app-name}
URL.
So, to make your greeter app available on the web add -Web
parameter to either 'Connect-PgletPageor
Connect-PgletApp` call:
Connect-PgletApp -Name 'greeter-app' -Web -ScriptBlock { <# ... #> }
As it's going to a public service the page name must be unique. One way is to prepend page name with "account" or "namespace", for example:
Connect-PgletApp -Name 'john/greeter-app' -Web -ScriptBlock { <# ... #> }
or just omit page name, so it will be randomly generated. Look at this article to understand how page naming works.