#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, но этот , похоже, может быть хорошим примером.