Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Proposed View-layer changes
- =============
- Friday, Juan, Tom, Yehuda and I had a meeting where we discussed the renderer APIs
- and some ideas we had for the view layer. Here is what we came up with:
- Overview
- ------------
- The purpose of these proposed changes is to lower the learning curve for users entering the
- SproutCore world and who are looking for quick feedback and an easy way to create custom views
- and a facility to create complex, themable views.
- Name Changes
- -------------
- - Renderer => RenderDelegate
- Views
- ---------------
- Our goal for the next version of SproutCore is to streamline the process of creating custom
- views. We've received a lot of feedback from the community regarding this process and we
- believe that simplifying it will be a huge benefit for people starting out with SproutCore.
- For the purpose of this discussion, we will talk about 3 levels of custom view generation from
- the most basic, to the most complex:
- 1) The one-off view:
- This kind of custom view is the most basic, has some displayProperties associated with it,
- and has a custom render() implementation. The user doesn't have to implement firstTime,
- and doesn't have to worry about updating the view when the displayProperty changes.
- each displayProperty will have an observer on it, and when it changes, we will update that DOM
- element's val with the new value of the displayProperty. The mapping between selector and
- displayProperty is an API that still has to be worked out, but the idea is that you can create
- a custom view like this:
- // THIS SAMPLE IS A WORK IN PROGRESS, THE API IS NOT FINAL
- myView: SC.View.design({
- displayProperties: 'fullName'.w(),
- render: function(context){
- context.push('<div class="fullName">Default Value</div>');
- }
- })
- As I mentioned, the API for mapping '.fullName' to the innerHTML is still an API that has to be
- worked out, it could be a simple mapping between a css selector and a displayProperty. We could
- use WebKit DOM bindings to create the mapping between displayProperty and DOM element, or it could
- use the classname of the div to associate it with a displayProperty.
- 2) A basic view, but with more complex handling
- In this case, the user is still building a basic view, but he wants to more closely manage how
- the view behaves when one of its displayProperties change.
- myView: SC.View.design({
- displayProperties: 'fullName'.w(),
- fullNameDidChange: function(newValue){
- this.$('.fullName').val(newValue);
- // THIS IS WHERE EXTRA PROCESSING WOULD GO
- }.observes('fullName'),
- render: function(context){
- context.push('<div class="fullName">Default Value</div>');
- }
- })
- 3) A complex, themable view
- When the user wants to make a re-usalbe, and/or themable view, then they start have to take more
- control over its generation and updates. This is where RenderDelegates come into play, discussed
- in the next section.
- RenderDelegates
- ---------------
- At a macro level, RenderDelegates separate the generation of a view from the business logic
- of the view. This allows us to modify how views are being generated based on the theme.
- By default, a view's renderDelegate would be null. In the base render function, SC.View
- will call the render method if possible.
- This is the only way to ensure backwards compatibility, since views _do_ frequently
- extend from other views (such as SC.ButtonView) and override the render() method,
- yet call sc\_super()—they do this, for instance, if they only want to modify certain
- styles rather than change everything.
- This is how we would implement SC.ButtonView in SproutCore, as a themeable view.
- SC.ButtonView = SC.View.extend({
- // Render delegate can be a string, object, or computed property.
- renderDelegate: 'button',
- mouseUp: function(evt) {
- ...
- },
- ...
- /* SC.View's init method: */
- init: function() {
- ...
- if (typeof this.renderDelegate === "string") {
- this.renderDelegate = this._renderDelegateLookupFor(this.renderDelegate);
- }
- },
- /* SC.View's render method: */
- render: function(context, firstTime) {
- var del = this.get('renderDelegate');
- if (del && firstTime) del.render(context);
- else if (del) del.update(context);
- if (!del) this.renderChildViews(context, firstTime);
- }
- });
- // Example 1, using an alternate style of button from the current theme.
- myRoundedButtonView = SC.ButtonView.extend({
- // you should NOT have to have a different renderDelegate just because
- // you are changing appearance. The theme will decide if that is needed.
- theme: 'rounded'
- });
- // Example 2, dynamically choosing a render delegate at runtime depending on
- // the browser's capabilities.
- myCanvasButtonView = SC.ButtonView.extend({
- // changing the theme will vary the renderDelegate as the theme feels is needed.
- theme: SC.platform.supportsCanvas ? 'mytheme-canvas' : 'mytheme'
- });
- // Example 3, the user just wants a quick override of SC.ButtonView without
- // implementing their own render delegate.
- myCustomButtonView = SC.ButtonView.extend({
- render: function(context, firstTime) {
- context.push('<div class="my-sweet-button">Click!</div>');
- }
- });
- What RenderDelegates Look Like
- --------------------------------
- Theme.Button = SC.RenderDelegate.extend({
- render: function(context) {
- context.text(this.get('title'));
- },
- update: function($) {
- $.text(this.get('title'));
- }
- }
- The .get() method will look at the RenderDelegate itself, and if the
- property on the renderDelegate is "undefined", will look up the property
- on the theme's dataDelegate if present (which would, if instantiated by
- an SC.View, be the view itself).
Add Comment
Please, Sign In to add comment