In this step you add a button and a controller that coordinates the button, label, and badge. You also start splitting your code into multiple libraries. Implementing multiple libraries is optional, but it's a good way to modularize your app.
Keywords: controller, module, click event, defining a library
→ Create a lib
directory at the same level as the web
directory.
You can do this at the command line, in a Finder,
or in Dart Editor.
In Dart Editor, right-click the top directory (s1_basics
),
choose New Folder...,
and specify the name lib
.
→ Under lib
, create a file named badge_controller.dart
.
To do this in Dart Editor, right-click the lib
directory,
choose New File...,
and specify the name badge_controller.dart
.
→ Under lib
, create a file named pirate_module.dart
.
Key information:
- The pub package layout conventions
specify the
lib
name and location. - The pub package manager looks under
lib
for any libraries that are imported withpackage:this_package
(where this_package is the current package's name, as declared in the pubspec). - Both
badge_controller.dart
andpirate_module.dart
will declare libraries.
To implement the controller for this app,
edit lib/badge_controller.dart
.
→ Add the following code to declare and import libraries.
library s1_basics.badge_controller; import 'package:angular/angular.dart';
If you're using Dart Editor, it warns you that the import is unused.
→ Create an empty class named BadgeController
, and give it a name
property.
... class BadgeController { String name = ''; }
→ Add an NgController
annotation to the class.
@NgController( selector: '[badge-controller]', publishAs: 'ctrl') class BadgeController {
Key information:
- The NgController annotation tells Angular that BadgeController is an Angular controller.
- The required
selector
argument defines the CSS selector that triggers the controller. It can be any valid CSS selector that does not cross element boundaries. - The
publishAs
argument specifies that the controller instance should be assigned to the current scope under the specified name.
→ Define some constant strings for the UI.
Class BadgeController { static const DEFAULT_NAME = 'Anne Bonney'; static const LABEL1 = 'Arrr! Write yer name!'; static const LABEL2 = 'Aye! Gimme a name!'; String name = ''; ...
Note that Dart Editor italicizes static values (and static methods, too).
→ Add an inputIsNotEmpty
getter, a label
getter, and a generateName()
method
to BadgeController.
String name = ''; bool get inputIsNotEmpty => name.trim().isNotEmpty; String get label => inputIsNotEmpty ? LABEL1 : LABEL2; generateName() { name = DEFAULT_NAME; } }
To register the BadgeController class,
you need to edit lib/pirate_module.dart
.
→ Add the following code to declare and import libraries.
library s1_basics.pirate_module; import 'package:angular/angular.dart'; import 'package:s1_basics/badge_controller.dart';
→ Add a subclass of Angular's Module class, and register BadgeController.
... class PirateModule extends Module { PirateModule() { type(BadgeController); } }
Key information:
- The Module class provides all of Angular’s built-in services and directives.
- The code
type(BadgeController)
adds BadgeController to the list of modules that Angular loads.
Now edit main.dart
,
so that it uses a PirateModule object.
→ Modify the main()
method to instantiate PirateModule.
import 'package:s1_basics/pirate_module.dart'; void main() { ngBootstrap(module: new PirateModule()); }
Now edit web/index.html
,
so that it uses the controller.
→ Add badge-controller
to the <body>
element.
<body badge-controller>
Key information:
- Thanks to the
badge-controller
selector, a BadgeController now controls every element under<body>
.
→ Move ng-cloak
up to the <body>
element.
<body badge-controller ng-cloak> ... <span id="badgeName">{{name}}</span>
→ Add a button
element under the input field.
<div class="widgets"> <div> <input type="text" id="inputName" maxlength="15" ng-model="name"> </div> <div> <button ng-click="ctrl.generateName()">{{ctrl.label}}</button> </div> </div>
Key information:
ng-click
is a built-in Angular directive that allows you to specify custom behavior when an element is clicked. In our example, it invokes thegenerateName()
method on the controller.- As
{{ctrl.label}}
shows, an Angular expression can refer to a getter.
→ Update data binding: replace name
with ctrl.name
.
<input type="text" id="inputName" maxlength="15" ng-model="ctrl.name"> ... <span id="badgeName">{{ctrl.name}}
→ Add a ng-disabled
directive to the button tag.
<button ng-click="ctrl.generateName()" ng-disabled="ctrl.inputIsNotEmpty">{{ctrl.label}}</button>
Key information:
-
ng-disabled
disables the element whenever the condition is true. In this case, the button is disabled whenever the input field contains text. -
Like
ng-click
,ng-disabled
is a built-in AngularDart directive.
→ Once the app is running, click the button.
"Anne Bonney" appears in the text field and badge, and the button becomes disabled.
→ Erase all text from the input field.
The button becomes enabled again.
Check your code against the files in s3_controller.
Don't worry about differences in package names.
Your files should contain the string s1_basics
wherever the files in s3_controller contain s3_controller
.