контроллеры плагинов jquery перезаписывают друг друга

#javascript #jquery #inheritance #jquery-plugins #scope

#javascript #jquery #наследование #jquery-плагины #область действия

Вопрос:

Я использую два плагина jquery — один для вкладок и один для галереи миниатюр — для создания галереи с вкладками. Все шло гладко, пока я не попытался ввести простой шаблон mvc. Теперь, каким-то образом, контроллер из любого плагина, инициализированного вторым, переопределяет первый, что приводит к большому количеству «new_view.tab не определен», поскольку первый плагин пытается использовать контроллер второго.

Вот код для вкладок

 ( function( $ ) {

// private vars
var settings;

var Model = ( function( ) {
    var pub = { };
        current_view = null;

    pub.get_current = function( ) {
        if( current_view ) {
            return current_view;
        } else {
            return false;
        }
    }
    pub.set_current = function( new_view ) {
        if( current_view ) console.log( 'current view is '   current_view.number );
        current_view = new_view;
        if( current_view ) console.log( 'current view is now '  current_view.number );
    }
    return pub;
})( );

var Controller = ( function( Model ) {
    var pub = { };
        model = Model;

    pub.update = function( new_view ) {
        console.log( 'tab controller update' );

        settings.update( model.get_current_view, new_view, function( callback ) {
            if( model.get_current( ) ) {
                model.get_current( ).tab.removeClass( settings.active_class );
            }
            model.set_current( new_view );
            new_view.tab.addClass( settings.active_class );
        });
    }
    pub.close = function( new_view ) {
        settings.close_callback(  );
        pub.model.set_current( null );
    }
    console.log( 'in tabs controller');
    console.log( pub );
    return pub;
})( Model );

console.log( 'in tabs')
console.log( Controller );

var View = ( function( Controller ) {
    var pub = { };
        controller = Controller;

    pub.update = function( new_view ) {
        controller.update( new_view );
    }

    pub.close = function( new_view ) {
        controller.update( new_view );
    }

    return pub;
})( Controller );

/* private closures */
function Tab( tab, tab_content, close, number ) {
    this.tab = tab;
    this.tab_content = tab_content;
    this.close = close;
    this.number = number;
    var Tab = this;

    // listeners
    tab.bind( 'click.tab', function( evnt ) {
        console.log( 'tab'   number   ' clicked' );
        evnt.preventDefault( );
        if( Tab.tab.hasClass( settings.active_class ) ) return;
        Tab.update( Tab );
    });

    close.bind( 'click.close', function( evnt ) {
        console.log( 'tab'   number   ' close clicked' );
        evnt.preventDefault( );
        Tab.close( Tab );
    });
}
Tab.prototype = View;

//public methods
var methods = {
    init : function( options ) {

        //defaults
        var defaults = {
            'tab_class': 'tab',
            'content_class' : 'content',
            'close_class': 'close',
            'active_class': 'active',
            'animate_open': function( arg ) {
                $( arg ).show( );
            },
            'animate_close': function( arg ) {
                $( arg ).hide( );
            },
            'tab_click_callback': function( ) { },
            'close_callback': function( ){ }
        };

        return this.each( function( ) {
            //update defaults with user options
            if( options ) {
                settings = $.extend( true, { }, defaults, options );
            } else {
                settings = defaults;
            }

            var container = $( this );
            var tabs = $( '.'   settings.tab_class, container ).children( );
            var content = $( '.'   settings.content_class, container );
            var close = $( '.'   settings.close_class, container );
            var len = tabs.length;
            var i = 0;
            tabs.each( function( index ) {
                var current_tab = $( this );
                new Tab( current_tab, $( content[ index ] ), $( close[ index ] ), index   1 );
            });
        });//end return
    },//end init
    destroy: function( ) {
        return this.each( function( ) {
            methods = null;
        });
    }
};//end methods

//add function to jquery
$.fn.id_tabs = function( method ) {
    if ( methods[method] ) {
        return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
        return methods.init.apply( this, arguments );
    } else {
        $.error( 'Method '    method   ' does not exist on jQuery.id_tabs' );
    }    
};

})(jQuery);
  

и для галереи

 (function($) {

var settings;

/* private */   
var Model = ( function( ) {
    var pub = { };
        current_view = null;

    pub.get_current = function( ) {
        if( current_view ) {
            return current_view;
        } else {
            return false;
        }
    }
    pub.set_current = function( new_view ) {
        if( current_view ) console.log( 'current view is '   current_view.number );
        current_view = new_view;
        if( current_view ) console.log( 'current view is now '  current_view.number );
    }
    return pub;
})( );

var Controller = ( function( Model ) {
    var pub = { };
        model = Model;

    pub.update = function( view ) {
        console.log( 'gallery controller update' );
        settings.update( model.current_view, view );
        if( model.get_current( ) ) {
            model.get_current( ).thumb.removeClass( settings.active_class );
        }
        model.set_current( view );
        view.thumb.addClass( settings.active_class );
    }
    console.log( 'in gallery controller');
    console.log( pub );
    return pub;
})( Model );

console.log( 'in gallery');
console.log( Controller );

var View = ( function( Controller ) {
    var pub = { };
        controller = Controller;

    console.log( 'in gallery view' );
    console.log( controller );

    pub.update = function( new_view ) {
        console.log( 'in gallery update');
        console.log( controller );
        controller.update( new_view );
    }
    return pub;
})( Controller );

function Thumb( thumb, pic, number ) {
    this.thumb = thumb;
    this.pic = pic;
    this.number = number;
    var Thumb = this;

    this.hide = function( callback ) {

        console.log( Thumb );
        settings.animate_out( Thumb, function( ) {
            if( callback ) callback( );
        });
    }
    this.show = function( callback ) {
        console.log( Thumb );
        settings.animate_in( Thumb, function( ) { 
            if( callback ) callback( );
        });
    }
    thumb.bind( 'click.thumb', function( evnt ) {
        console.log( 'pic'   number   ' clicked' );
        evnt.preventDefault( );
        if( Thumb.thumb.hasClass( settings.active_class ) ) return;
        Thumb.update( Thumb );
    });
};
Thumb.prototype = View;

/* public */
var methods = {
    init : function( options ) {

        //defaults
        var defaults = {
            'container_id': 'content',
            'active_class' : 'active',
            'movie_class': 'movie',
            'animate_in': function( arg ) {
                $( arg ).fadeIn( );
            },
            'animate_out': function( arg, callback ) {
                $( arg ).fadeOut( );
                callback( );                    
            },
            'update' : function( current, new_view, callback) {
                if( current ) current.pic.fadeOut( );
                new_view.pic.fadeIn( );
                if( callback ) callback( );
            }
        };

        return this.each( function( ) {
            //update defaults with user options
            if( options ) {
                settings = $.extend( true, { }, defaults, options );
            } else {
                settings = defaults;
            }

            var obj = $( this );
            var li = $( 'li', obj );
            var obj_class = obj.attr( 'id' );
            var container = $( '#'   settings.container_id );
            var content = $( '.'   obj_class, container );

            li.each( function( index ) {
                var current_li = $( this );
                var current_content = $( content[ index ] );
                var src = current_li.children( ).children( ).hide( ).attr( 'src');
                var href = current_li.children( ).attr( 'href');
                current_li.css( 'background-image', 'url('   src   ')' );
                current_content.css( 'background-image', 'url('   href   ')' );         
                new Thumb( current_li, current_content, index );
            });
        });//end return
    },//end init
    destroy: function( ) {
        return this.each( function( ) {
            methods = null;
        });
    },
    hide_current: function( callback ) {
        console.log( 'hide_current' );
        if( Model.current_view ) {
            settings.animate_out( Model.current_view, function( ) {
                Model.current_view.thumb.removeClass( settings.active_class );
                Model.current_view = null;
                if( callback ) callback( );
            });
        } else {
            callback( );
        }
    }
};//end public methods

//add function to jquery
$.fn.id_gallery = function( method ) {
    if ( methods[method] ) {
        return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
        return methods.init.apply( this, arguments );
    } else {
        $.error( 'Method '    method   ' does not exist on jquery.id_gallery' );
    }    
};

})(jQuery);
  

Я предполагаю, что что-то ползет вверх по цепочке областей видимости, но с «Контроллером», определенным локально внутри каждого замыкания, я не понимаю, как.

Еще более неприятно, что я запустил jsFiddle и заставил его там работать, но ни за что на свете не могу понять разницу между этим и моим производственным кодом. http://jsfiddle.net/noTxt/vWsJw

пожалуйста, помогите

Ответ №1:

Я не знаком с этим стилем написания плагинов для jQuery, поэтому, возможно, я упускаю что-то очевидное, но я вижу, что существует глобальная переменная с именем «controller», которая назначается в обоих местах. Возможно, проблема в этом?

Комментарии:

1. так оно и было. Человек, который убивал меня весь вчерашний день. Спасибо.

2. Я новичок в написании плагинов jquery и javascript в целом. Но я много читал об этом — (в основном в блогах и тому подобном) — так что я как бы собрал эти первые попытки вместе, исходя из того, что имело для меня смысл. Если вы знаете лучший способ или как можно улучшить этот стиль, я хотел бы услышать ваши мысли.

3. Я никогда не писал плагин jQuery, специально использующий шаблон MVC, но этот , похоже, может быть хорошим примером.