Seaside: Composing Components

Damien Cassou, St├ęphane Ducasse and Luc Fabresse http://stephane.ducasse.free.fr

Remember: Seaside

Roadmap

3 mechanisms to reuse components:

Roadmap

3 mechanisms to reuse components:

Remember: Counter / TwitterCounter

WACounter

WATwitterCounter

How to Build a Multi-Counter Application?

WAMultiCounter

WAMultiCounter

WAExampleComponent subclass: #WAMultiCounter
    instanceVariableNames: 'counters'
    classVariableNames: ''
    package: 'Seaside-Examples-Misc'.
	
WAMultiCounter >> initialize
    super initialize.
    counters := (1 to: 5) collect: [ :each | WACounter new ].

WAMultiCounter >> children
    ^ counters
		
WAMultiCounter >> renderContentOn: html
    counters do: [ :each | html render: each ]

How to Compose Components?

A composite:

Aggregating Different Subcomponents

WAExampleComponent subclass: #MyApp
    instanceVariableNames: 'children'
    classVariableNames: ''
    package: 'Seaside-Examples-Misc'.

MyApp >> initialize
    super initialize.
    children := { Greeter new. 
		 WATwitterCounter new. WACounter new }.

MyApp >> children
    ^ children

MyApp >> renderContentOn: html
    children do: [ :each | html render: each ]
        separatedBy: [ html line ]

Aggregating Different Subcomponents

Rendering Only One Subcomponent

Rendering Only One Subcomponent

MyApp >> initialize
    "..."
	 selectedChild := children first

MyApp >> renderContentOn: html
	self renderMenuOn: html.
	html render: selectedChild 

MyApp >> renderMenuOn: html
    html unorderedList: [ 
        self children doWithIndex: [ :child :i |
            html listItem: [ 
                html anchor 
                    class: 'selected' if: child = currentComponnent ;
                    callback: [ selectedChild := self children at: i ];
						  with: child className  
    ] ] ]

Roadmap

3 mechanisms to reuse components:

call:

call:

answer:

answer:

call:/answer: Example

call:/answer: Example

WATwitterCounter >> renderContentOn: html
    "..."
    html tbsButton beDefault; 
        callback: [ self setCountToUserValue ]; with: 'Set Value'
    "..."

WATwitterCounter >> setCountToUserValue
    | webDialog newCounterValueByUser |
    webDialog := WAInputDialog new
        addMessage: 'Enter a new value for the counter:';
        default: '0';
        label: 'Ok';
        yourself.
    newCounterValueByUser := self call: webDialog.
    count := newCounterValueByUser asNumber

WAInputDialog Internals

WAInputDialog

WAInputDialog >> renderContentOn: html
	html form
		defaultAction: [ self answer: value ];
		with: [
			html div: [
				html textInput on: #value of: self.
				html space.
				html submitButton
					callback: [ self answer: value ];
					text: self label ] ]

Roadmap

3 mechanisms to reuse components:

Encapsulating Workflows

Tasks are simple components:

A Simple Web Task

WATask subclass: #Adder
    instanceVariableNames: ''
    classVariableNames: ''
    package: 'SeaExample'
Adder >> go
    | value1 value2 |
    value1 := self request: 'first number'.
    value2 := self request: 'second number'.
    self inform: value1 asNumber + value2 asNumber
WAAdmin register: self asApplicationAt: 'Adder'.

How request: is Implemented?

request: uses call: / answer: on a WAInputDialog

WAComponent >> request: aRequestString label: aLabelString default: aDefaultString onAnswer: aBlock
	"Display an input dialog with the question aRequestString, the button label aLabelString and the default string aDefaultString. Passes the answer into aBlock."
	
    self 
         call: (WAInputDialog new
            addMessage: aRequestString;
            default: aDefaultString;
            label: aLabelString;
            yourself)
        onAnswer: aBlock	

How inform: is Implemented?

inform: uses call: / answer: on a WAFormDialog

WAComponent >> inform: aString onAnswer: aBlock	
    "Display a dialog with aString to the user until he clicks the ok button. Continue by evaluating aBlock."

    self 
        call: (WAFormDialog new
            addMessage: aString;
            yourself)
        onAnswer: aBlock

Stepping Back

Conclusion

Seaside provides:

/