In this step, you build a registration form displayed in a modal panel.
Keywords: form, data-binding, custom events
This component will further contain all the components binded together to make the game work. For now you will use it to test the components you will create in this step.
→ Create a new file web/game.html
, with the following content:
<link rel="import" href="packages/polymer/polymer.html">
<polymer-element name="risk-game">
<template>
</template>
<script type="application/dart" src="game.dart"></script>
</polymer-element>
→ Create a new file web/game.dart
, with the following content:
import 'dart:html';
import 'package:polymer/polymer.dart';
@CustomTag('risk-game')
class RiskGame extends PolymerElement {
RiskGame.created() : super.created();
}
→ In web/index.html
add a <risk-game>
:
<link rel="import" href="game.html">
<!-- .... -->
<risk-game></risk-game>
The registration form will appear in a modal panel. We will first create a <risk-modal>
element that will allow to display some contents in a modal panel.
→ Create a new file web/modal.html
, with the following content:
<link rel="import" href="packages/polymer/polymer.html">
<polymer-element name="risk-modal">
<template>
<style>
:host {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 1000;
background-color: rgba(256,256,256,0.8);
}
#title {
border-bottom: 1px solid lightgray;
}
#panel {
background-color: white;
border: 1px solid lightgray;
border-radius: 10px;
padding: 10px;
width: 80%;
max-width: 768px;
margin: 5% auto 0;
max-height: 80%;
overflow-x: hidden;
overflow-y: auto;
}
#close {
float: right;
border: 1px solid lightgray;
}
</style>
<div id="panel">
<h4 id="title">{{ header }}</h4>
<content></content>
</div>
</template>
<script type="application/dart" src="modal.dart"></script>
</polymer-element>
→ Create a new file web/modal.dart
, with the following content:
import 'package:polymer/polymer.dart';
@CustomTag('risk-modal')
class RiskModal extends PolymerElement {
@published
String header;
RiskModal.created(): super.created();
}
Key information:
- The
<content>
tag allows to inject the content provided inside<risk-modal></risk-modal>
. This<content>
may also accept aselect
attribute to only grab some specific parts of the original HTML content.
→ Use this new component in web/game.html
to display some basic text:
<link rel="import" href="modal.html">
<!-- .... -->
<risk-modal header="Title of the modal">Content as <b>HTML</b></risk-modal>
→ Run in Dartium
You should see a modal panel with the title you provided and the original content displayed.
You will now improve the modal panel to allow it to be closable.
→ In web/modal.html
replace the <div id="panel">
with the following:
<div id="panel">
<template if="{{ closable }}">
<button id="close" on-click="{{ close }}">×</button>
</template>
<h4 id="title">{{ header }}</h4>
<content></content>
</div>
→ In web/modal.dart
add the following members:
@published
bool closable;
// ....
close() => fire('close');
Key information:
- The
<risk-modal>
element can now be closable or not depending on itsclosable
attribute. - By clicking on the close button a CustomEvent will be fired on the element. This event can be handled outside of the element with:
<risk-modal on-close="...">
.
To test this new behaviour add closable="{{ true }}" on-close="{{ close }}"
in web/game.html
and close(CustomEvent e, var detail, Element target) => target.remove();
in web/game.dart
. Run in Dartium.
→ Create a new file web/registration.html
, with the following content:
<link rel="import" href="packages/polymer/polymer.html">
<polymer-element name="risk-registration">
<template>
<link rel="stylesheet" href="packages/bootstrap_for_pub/3.1.0/css/bootstrap.min.css">
<link rel="stylesheet" href="packages/bootstrap_for_pub/3.1.0/css/bootstrap-theme.min.css">
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-3 control-label">Name</label>
<div class="col-sm-9">
<input class="form-control" value="{{ name }}">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Avatar</label>
<div class="col-sm-9">
<select selectedIndex="{{ avatarSelectedIndex }}">
<option template repeat="{{ avatar in avatars }}">{{ avatar }}</option>
</select>
<br>
<br>
<img alt="avatar" class="img-rounded" _src="img/avatars/{{ avatar }}">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Country color</label>
<div class="col-sm-9">
<input type="color" value="{{ color }}">
</div>
</div>
<hr>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button class="btn btn-primary">Join</button>
</div>
</div>
</div>
</template>
<script type="application/dart" src="registration.dart"></script>
</polymer-element>
→ Create a new file web/registration.dart
, with the following content:
import 'dart:math';
import 'package:polymer/polymer.dart';
final _random = new Random();
@CustomTag('risk-registration')
class RiskRegistration extends PolymerElement {
final List<String> avatars = ['ahmadi-nejad.png', 'bachar-el-assad.png',
'caesar.png', 'castro.png', 'hitler.png', 'kadhafi.png', 'kim-jong-il.png',
'mao-zedong.png', 'mussolini.png', 'napoleon.png', 'pinochet.png',
'saddam-hussein.png', 'staline.png'];
@observable
String name;
@observable
String avatar;
@observable
String color;
RiskRegistration.created(): super.created() {
avatar = (avatars.toList()..shuffle(_random)).first;
color = '#' + new List.generate(6, (_) => (6 + _random.nextInt(10)).toInt(
).toRadixString(16)).join();
}
int get avatarSelectedIndex => avatars.indexOf(avatar);
set avatarSelectedIndex(int index) {
avatar = avatars[index];
}
}
→ Use this new <risk-registration>
in web/game.html
:
<link rel="import" href="registration.html">
<!-- .... -->
<risk-modal header="Player registration">
<risk-registration></risk-registration>
</risk-modal>
→ Run in Dartium.
Key information:
- The two-way data binding makes forms really easy to handle. Any change in the input is reflected directly on the property of the class.
<select>
binding is done with itsselectedIndex
attribute. Theget avatarSelectedIndex
/set avatarSelectedIndex
are used for translation between selectedIndex and value.<option>
elements use a specific syntax for repeat:<option template repeat="{{ avatar in avatars }}"
instead of<template repeat="{{ avatar in avatars }}"><option>{{ avatar }}</option></template>
. Some elements (like<option>
,<tr>
,<td>
) have parsing rules that prohibit a<template>
tag.
To improve the form submission you will enable the Join button only when all the informations are provided.
→ In web/registration.html
replace the Join button with the following code:
<button class="btn btn-primary" disabled?="{{ !isValid }}">Join</button>
→ In web/registration.dart
add the following code :
class RiskRegistration extends PolymerElement {
// .....
RiskRegistration.created(): super.created() {
// .....
// notif isValid change
notifyIsValid() => notifyPropertyChange(#isValid, null, isValid);
onPropertyChange(this, #name, notifyIsValid);
onPropertyChange(this, #avatar, notifyIsValid);
onPropertyChange(this, #color, notifyIsValid);
}
// .....
bool get isValid => [name, avatar, color].every((v) => v != null && v.trim(
).isNotEmpty);
}
→ Run in Dartium.
Key information:
- The
?=
syntax indisabled?="{{ !isValid }}"
allows to make the presence ofdisabled
conditionnal to the expression provided. #name
is equivalent toconst Symbol('name')
. A Symbol object represents an operator or identifier declared in a Dart program (mainly needed for minification).- onPropertyChange is used to observe changes on a particular member and to trigger actions.
- notifyPropertyChange is used to inform that the field name of this object has been changed.
notifyIsValid
is an inline function only available in the constructor. It's like definingvar notifyIsValid = () => notifyPropertyChange(#isValid, null, isValid);
.
→ In web/registration.html
add on-click="{{ join }}"
to the Join button.
→ In web/registration.dart
add the following join
method that will be called on Join click:
join() => fire('done', detail: {
'name': name,
'avatar': avatar,
'color': color,
});
Unlike the close
event fired by <risk-modal>
this done
event comes with additionnal informations. The optional named parameter detail
allows to pass any kind of contextual datas associated with the event.
→ In web/game.html
add on-done='{{ joinGame }}'
and implement joinGame
in web.game.dart
to print the detail in the console.
Check your code against the files in s7_enrollment (diff).