UI Architecture & Lessons Learned
Welcome to the web
MagicBullet.js?
The web is hostile.
UI programming is hard.
AngularJS
It compiles your DOM
& asks you to use your markup as a DSL
- ui-dropdown
- ui-tile
- ui-selectable
- case-card
- case-properties
- alarm-card
- widget
- ng-if
- ng-class
- ng-include
- ng-show
- ng-controller
- input
- form
It sets up "scopes" (view models)
<html ng-app="ToDo">
<div ng-controller="ToDoCtrl">
<ul>
<li ng-repeat="item in todos">{{item.text}}
<small>({{$index}} of {{todos.length}})</small>
</li>
</ul>
<form ng-submit="addTodo()">
<input type="text" ng-model="newTodo"/>
</form>
</div>
</html>
angular.module('ToDo')
.controller 'ToDoCtrl', ($scope) ->
$scope.newToDo = ''
$scope.items = [
{id:0, text : 'Get groceries'}
{id:1, text : 'Pick up kids'}
]
$scope.addToDO = ->
$scope.items.push {id : $scope.items.length, text : $scope.newToDo}
$scope.newToDo = ''
About scopes though...
they are tied to the structure of your dom
<html ng-app>
<div ng-controller="ToDoCtrl">
<ul>
<li ng-repeat="item in todos">{{item.text}}
<small>({{$index}} of {{todos.length}})</small>
</li>
</ul>
<form ng-submit="addTodo">
<input type="text" ng-model="newTodo"/>
</form>
</div>
</html>
It gives you change detection
$scope.$watch "themeName", (newTheme, prevTheme)->
if newTheme != prevTheme
htmlTag
.removeClass(prevTheme)
.addClass(newTheme)
$scope.setThemeName(newTheme)
<span ng-show="!widget.settingsOpen">
<span>
{{widget.name}}
</span>
<small ng-class="{redTimeScale: widget.settings.timeScale.showWarning}">
{{widget.settings.timeScale.get()}}
</small>
</span>
$scope.$on 'nubChange', (event, nub, index, nubsID) -> #...
$scope.$on 'dragResizeEnd', (e, elem, width, resizeID) -> #...
$scope.$on 'filtered-select-up', (e, data) -> #...
$scope.$on 'filtered-select-selected', (event, data) -> #...
$rootScope.$on 'DRILL-DOWN', -> #...
<div id="CaseDetails" ng-controller="CaseDetailsController">
Case Details
<div case-card></div>
<div case-evidence></div>
</div>
class CaseUI
showCaseCard : () ->
caseToShow = this.parent.parent.case
# ...
class CaseUI
showCaseCard : (caseToShow) ->
# ...
Uhhhhhhh....
- MV*?
- Angular watches scale poorly, but...
- Other eventing mechanisms can make spaghetti!
- Scope inheritance creates implicit data contracts
- We are struggling with separation of concerns
In summary...
Lines of separation
- Stop treating the scope as your model
Death to scopes!
- Isolate representation of state into models
- Interactions affect the model
- Views watch the model
Make your own models
- Treat your directives like an api
- Isolate scopes, require explicit input
Death to scope inheritance!
<case-card case-id="123"></case-card>
<ui-date-picker ng-model="ctrl.dateModel" format="MM/DD/YY"></ui-date-picker>
<ui-dropdown
ui-dropdown-keys="{display: 'name', selected: 'name' }"
ng-model="example.selected"
></ui-dropdown>
- Inheritance is hard to trace
- Prefer composition with services
Death to inheritance!
angular.module('Analyze').controller 'AnalyzeCtrl', (
$scope, $window, Socket, $location, $rootScope, $timeout, $filter,
MasterQuery, EVENT_INDEX_ID, Layout, Task, User,
CurrentView, VolumeChart, Drilldown, AuditLogModel, AuditLogSvc) ->
# ...
- Watches & digest are responsible for the view
- Use observability of models for business logic
Death to watches!
Putting it together...
Model
Controller
View
Services
Drawing time!
UI Architecture & Lessons Learned
By autoric
UI Architecture & Lessons Learned
- 525