Composeable Views in Seaside
By Ramon Leon - 11 January 2007 under Ruby, Seaside
So I was talking to a Ruby on Rails buddy of mine today, and he asked me if Seaside could do views composed from other views like Rails. I thought I'd throw up a quick sample in case any one else wonders how this is done. It's a very common thing to do, so common, I'd never considered writing about it until today.
It's very basic, contains a header, footer, main control, and a result control to show how lists of items can each be their own views. MVC is so ordinary in Seaside I forget how novel it is to some other frameworks.
I start off creating a simple header control with one method...
Header>>renderContentOn: html html div: 'Header'
Then a footer control doing the same, though each could be much more complex...
Footer>>renderContentOn: html html div: 'Footer'
Ok, now I'll create a class for showing the results of a query, with each result being a component of its own...
ClassResult>>modelClass: aModel modelClass := aModel ClassResult>renderContentOn: html html heading: modelClass name level: 3. html div: ((modelClass allSubclasses collect: [:each | each name]) join: ', ')
I just printed the class name, and under it, all its subclasses names separated by a comma.
Now for the main root component that will be composed of these subcomponents...
ClassUsage class>>canBeRoot ^true ClassUsage>>initialize "setup sub components in instance variables, for the results, I'm grabbing all the subclasses of WAComponent that have subclasses themselves and then sorting them by how many subclasses they have, and then using them as the model in a view, and storing the list of views for later rendering." super initialize. header := Header new. footer := Footer new. results := ((WAComponent allSubclasses reject: [:each | each allSubclasses isEmpty]) asSortedCollection: [:a :b | a allSubclasses size > b allSubclasses size]) collect: [:each | ClassResult new modelClass: each] ClassUsage>>children "return all sub components as a list" ^{ header. footer } , results asOrderedCollection ClassUsage>>renderContentOn: html html render: header. (html div) class: #body; with: [results do: [:each | html render: each]]. html render: footer
That's about it. I setup the subcomponents in the initialize, and then by explicitly not calling super renderContentOn, I then have the option of when and where to render subcomponents.
Comments (automatically disabled after 1 year)
I'm not actually that familiar with Rails, beyond discussing it with Rails developers, nor do I have any intentions of learning it deeply, I already know Seaside and couldn't be happier with a framework. Rails is still dealing with request/response and doing things the old fashioned way.
Partials in Rails are its big weakness next to Seaside. I was working to convince some people that Seaside is awesome and my big winning argument was "render :partial == GOTO."