Skip to content

Proposal for DeftJS enhancements needs review #78

@jacobg

Description

@jacobg

This message is mainly for John...

I'd like to enhance DeftJS to do the following:

  • Support view controller behaviors (basically mix-ins) where each behavior may define controls, injections and observers that get merged into the view controller object.
  • Deft.mvc.ViewController will iterate through each subclass, and do the following for each subclass:
    • merge controls into object
    • perform injections
    • merge unique behaviors

Below is basically what the code changes to Deft.mvc.ViewController look like. Please let me know what you think. Is this the right approach, or do you recommend something different? Will you accept a pull request if I go with this?

One note is that I took a different approach to merging controls than is already done with observers. That is, although observers are merged at class definition time, I chose to merge controls at class creation time. I made that decision, because in my own application, I find this to be more useful to be able to set/customize controls in the constructor.

Thanks.

Ext.define( 'Deft.mvc.ViewController',

    constructor: ( config = {} ) ->

        @mergedBehaviors = []
        @mergeParents()
        if @behaviors then @mergeBehaviors( @behaviors )

        if config.view
            @controlView( config.view )
        @initConfig( config ) # Ensure any config values are set before creating observers.
        if Ext.Object.getSize( @observe ) > 0 then @createObservers()

        return @

    ###*
    * @private
    * This is a specialized clone algorithm that goes two levels deep, and maintains object references.
    ###
    cloneControl: (control) ->
        if !Ext.isObject(control) then return control # probably null or undefined

        clone = {}

        for key, value in control
            if Ext.isObject(value)
                subClone = {}
                for key2, value2 in value
                    subClone[key2] = value2
                clone[key] = subClone
            else
                # probably true or a string selector
                clone[key] = value

        return clone

    ###*
    * @private
    ###
    mergeControls: ( target, source ) ->
        if !Ext.isObject( target ) then return source
        if !Ext.isObject( source ) then return target

        # clone target, so we can apply the source to it
        # without affecting the class prototype
        merged = @cloneControl( target )

        for key, value in source
            if key in merged and Ext.isObject( merged[key] )
                if Ext.isObject(value)
                    Ext.apply(merged[key], value)
                # else do nothing, because target value is object and source is not
            else
                merged[key] = value

        return merged

    ###*
    * @private
    ###
    mergeBehaviors: ( behaviors ) ->

        control = @control

        if Ext.isArray( behaviors ) or Ext.isObject( behaviors )

            for behaviorName in behaviors when behaviorName not in @mergedBehaviors
                behavior = Ext.ClassManager.get( behaviorName )

                if not behavior
                    # Doing syncRequire for each behavior in the contructor is not the most efficient way to
                    # load classes, but Deft is already doing this anyway for the controller classes. The
                    # optimal scenario is where the developer pre-loads what he needs.
                    Ext.syncRequire( behaviorName )
                    behavior = Ext.ClassManager.get( behaviorName )

                prototype = behavior.prototype

                #
                # Now do merge behavior introl class, which involves:
                #    * merge controls (controller class takes precedence)
                #    * merge observers (controller class takes precedence)
                #    * apply behavior injections
                #    * merge rest of behavior class (controller class takes precedence)
                #
                control = @mergeControls( prototype.control, control )
                @observe = Deft.mvc.Observer.mergeObserve( prototype.observe, @observe )

                if Ext.isObject( prototype.inject ) then Deft.Injector.inject( prototype.inject, @, false )

                Ext.mergeIf( @, prototype )

                @mergedBehaviors.push(behaviorName)

        @control = control

    ###*
    * @private
    ###
    mergeClass: ( Class ) ->

        # this object takes precedence over Class, because Class is a parent class
        @control = @mergeControls( @control, Class.control )

        # note that we do not merge observers, because normally that gets done directly
        # in the constructor for each class in the class hierarchy

        if Ext.isObject( Class.inject ) then Deft.Injector.inject( Class.inject, @, false )

        @mergeBehaviors( Class.behaviors )

    ###*
    * @private
    ###
    mergeParents: ->

        superclass = @superclass

        while Ext.isObject( superclass ) and superclass.$className != arguments.callee.$owner.$className
            @mergeClass(superclass)
            superclass = superclass.superclass

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions