Skip to content

Commit 15b0fcf

Browse files
crud example
1 parent a714ffc commit 15b0fcf

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

docs/dsl-client/hyper-component.md

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,155 @@ However it gets a little tricky if you are using the react-rails gem. Each vers
14301430
14311431
```
14321432

1433+
## Single Page Application CRUD example
1434+
1435+
Rails famously used scaffolding for Model CRUD (Create, Read, Update and Delete). There is no scaffolding in Hyperstack today, so here is an example which demonstrates how those simple Rails pages would work in Hyperstack.
1436+
1437+
This examples uses components from the [Material UI](https://material-ui.com/) framework but the principals would be similar for any framework (or just HTML elements).
1438+
1439+
In this example, we will have a table of users and the ability to add new users or edit a user from the list.
1440+
1441+
Firstly the table of users:
1442+
1443+
```ruby
1444+
class UserIndex < HyperComponent
1445+
render(DIV, class: 'mo-page') do
1446+
UserDialog(user: User.new) # this will render as a button to add users
1447+
Table do
1448+
TableHead do
1449+
TableRow do
1450+
TableCell { 'Name' }
1451+
TableCell { 'Gender' }
1452+
TableCell { 'Edit' }
1453+
end
1454+
end
1455+
TableBody do
1456+
user_rows
1457+
end
1458+
end
1459+
end
1460+
1461+
def user_rows
1462+
User.each do |user| # note User is a Hyperstack model (see later in the Isomorphic section)
1463+
TableRow do
1464+
TableCell { "#{user.first_name} #{user.last_name}" }
1465+
TableCell { user.is_female ? 'Female' : 'Male' }
1466+
TableCell { UserDialog(user: user) } # this will render as an edit button
1467+
end
1468+
end
1469+
end
1470+
end
1471+
```
1472+
1473+
Then we need the actual Dialog (Modal) component:
1474+
1475+
```ruby
1476+
class UserDialog < HyperComponent
1477+
param :user
1478+
1479+
before_mount do
1480+
@open = false
1481+
# we care copying the model instance variables to local instance variables
1482+
# because we do not want updates made to the model to be reflected in the
1483+
# underlying table as the user types. If you did want this, you would not
1484+
# need to do this - just use the model itself
1485+
@first_name = @User.first_name
1486+
@last_name = @User.last_name
1487+
@is_female = @User.is_female
1488+
end
1489+
1490+
render do
1491+
# notice that the same component renders in three different ways
1492+
# as an Add button, and Edit button or a Dialog
1493+
if @open
1494+
render_dialog
1495+
else
1496+
edit_or_new_button.on(:click) { mutate @open = true }
1497+
end
1498+
end
1499+
1500+
def render_dialog
1501+
Dialog(open: @open, fullWidth: false) do
1502+
DialogTitle do
1503+
'User'
1504+
end
1505+
DialogContent do
1506+
content
1507+
error_messages if @User.errors.any?
1508+
end
1509+
DialogActions do
1510+
actions
1511+
end
1512+
end
1513+
end
1514+
1515+
def edit_or_new_button
1516+
if @User.new?
1517+
Fab(size: :small, color: :primary) { Icon { 'add' } }
1518+
else
1519+
Fab(size: :small, color: :secondary) { Icon { 'settings' } }
1520+
end
1521+
end
1522+
1523+
def content
1524+
FormGroup(row: true) do
1525+
TextField(label: 'First Name', value: @first_name.to_s).on(:change) do |e|
1526+
mutate @first_name = e.target.value
1527+
end
1528+
TextField(label: 'Last Name', value: @last_name.to_s).on(:change) do |e|
1529+
mutate @last_name = e.target.value
1530+
end
1531+
end
1532+
1533+
BR()
1534+
1535+
FormLabel(component: 'legend') { 'Gender' }
1536+
RadioGroup(row: true) do
1537+
FormControlLabel(label: 'Male',
1538+
control: Radio(value: false, checked: !@is_female).as_node.to_n)
1539+
FormControlLabel(label: 'Female',
1540+
control: Radio(value: true, checked: @is_female).as_node.to_n)
1541+
end.on(:change) do |e|
1542+
# for some unknown reason Radio() returns a String
1543+
mutate @is_female = e.target.value == 'true' ? true : false
1544+
end
1545+
end
1546+
1547+
def actions
1548+
Button { 'Cancel' }.on(:click) { cancel }
1549+
1550+
return unless ready_to_save?
1551+
Button(color: :primary, variant: :contained, disabled: (@User.saving? ? true : false)) do
1552+
'Save'
1553+
end.on(:click) { save }
1554+
end
1555+
1556+
def save
1557+
@User.update(first_name: @first_name, last_name: @last_name, is_female: @is_female).then do |result|
1558+
mutate @open = false if result[:success]
1559+
end
1560+
end
1561+
1562+
def cancel
1563+
mutate @open = false
1564+
end
1565+
1566+
def error_messages
1567+
@User.errors.full_messages.each do |message|
1568+
Typography(variant: :h6, color: :secondary) { message }
1569+
end
1570+
end
1571+
1572+
def ready_to_save?
1573+
return false if @first_name.to_s.empty?
1574+
return false if @last_name.to_s.empty?
1575+
return false if @is_female.nil?
1576+
return true if @first_name != @User.first_name
1577+
return true if @last_name != @User.last_name
1578+
return true if @is_female != @User.is_female
1579+
end
1580+
end
1581+
```
14331582

14341583
## Elements and Rendering
14351584

0 commit comments

Comments
 (0)