This code is an example from Marionette:
AppLayout = Backbone.Marionette.Layout.extend(
{
template: "#layout-template",
regions:
{
menu: "#menu",
content: "#content"
}
});
var layout = new AppLayout();
layout.menu.show(new MenuView());
layout.content.show(new MainContentView());
The last two lines confuse me. Why doesn't it read:
layout.regions.menu.show(new MenuView());
layout.regions.content.show(new MainContentView());
Can someone please explain why layout.menu works and layout.regions.menu doesn't?
What if I wanted to access template? Wouldn't that be layout.template? template and regions are at the same depth inside layout.
Here is the constructor function from the marionette code:
// Ensure the regions are avialable when the `initialize` method
// is called.
constructor: function () {
this._firstRender = true;
this.initializeRegions();
var args = Array.prototype.slice.apply(arguments);
Marionette.ItemView.apply(this, args);
},
I believe it was implemented that way because 'layout.menu' is shorter and simpler than 'layout.regions.menu'. Looks like you expected the literal "#menu" to be replaced with a region manager object.
The options you passed in when creating the view, including the template, can be found in layout.options. So in your case layout.options.template should equal '#layout-template', and the regions definition hash would be at layout.options.regions... still the same level.
Unless there is more to the example then you are showing like the Backbone.Marionette.Layout methods, then its not accessing regions.menu like you think it is.
With just the code you have provided the code above is actually creating a menu attribute, which then has a show attribute so your layout object would actually look like this:
layout {
menu : {
show : new MenuView
},
content : {
show : new MainContentView
},
template: "#layout-template",
regions:
{
menu: "#menu",
content: "#content"
}
}
In javascript the (dot) operator can be used to access a property of an attribute or if no property with that name exists then it will create that property.
I'm not familiar with the backbone.js framework but my guess is that they provide for skipping part of the property lookup chain. which means that the above would end up producing this as your layout object:
layout {
template: "#layout-template",
regions:
{
menu : {
show : new MenuView
},
content : {
show : new MainContentView
}
}
}
But again that's just a guess on my part since I don't use backbone.
You can learn more about the object model and how it works with inheritance right here.
Related
Is it possible to both override and mixin a class?
I tried applying the mixin inside the constructor, but it is not adding he properties/functions to the override.
I thought mixins were implicitly applied to the class referring to them?
Mixin
Ext.define('App.mixins.VTypes', {
//extend: 'Ext.mixin.Mixin',
timeTest : /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i,
time: function(v, field) { return this.timeTest.test(v); },
timeText: 'Not a valid time. Must be in the format "12:34 PM".',
timeMask: /[\d\s:amp]/i
});
Override
Ext.define('App.overrides.VTypes', {
override : 'Ext.form.field.VTypes',
mixins : {
vtypes : 'App.mixins.VTypes'
},
IPAddressTest : /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
IPAddress: function(v) { return this.IPAddressTest.test(v); },
IPAddressText: 'Must be a numeric IP address, for eg: 111.1.1.1',
IPAddressMask: /[\d\.]/i,
constructor : function() {
Ext.apply(this, this.mixins.vtypes);
this.callParent(arguments);
}
});
Here's the Sencha Fiddle I am working on.
You can not apply a mixin to Ext.form.field.VTypes easily. In fact, your constructor never gets called (Take a look at the source code of Ext.form.field.VTypes). Additionally, Ext.form.field.VTypes is defined as a singleton.
The definition of Ext.form.field.VTypes looks very different to a class definition for e.g. a GridPanel. It behaves more like a pure JS "class" definition, thus your mixin gets never applied as it would for a "normal" ExtJS class.
Nevertheless, you can easily simulate a mixin behavior by your own:
var ip = {
IPAddressTest : /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
IPAddress: function(v) { return this.IPAddressTest.test(v); },
IPAddressText: 'Must be a numeric IP address, for eg: 111.1.1.1',
IPAddressMask: /[\d\.]/i
};
var time = {
timeTest : /^([1-9]|1[0-9]):([0-5][0-9])(\s[a|p]m)$/i,
time: function(v, field) { return this.timeTest.test(v); },
timeText: 'Not a valid time. Must be in the format "12:34 PM".',
timeMask: /[\d\s:amp]/i
};
Ext.apply(Ext.form.field.VTypes, ip);
Ext.apply(Ext.form.field.VTypes, time);
Here, you spare the override and just apply the object definition to the Ext.form.field.VTypes singleton. I think, doing it that way is even more readable and understandable (but that's just my opinion).
I can create and display multiple dynamic codemirror instances, but I CANT reference them with the below code. Think the issue is that I need to create a dynamic function name. (Not 100% on how to do so here)
var function (somefilename, xml){
var instanceName = somefilename + 'Editor';
require([
'codemirror/lib/codemirror','codemirror/mode/xml/xml',
'codemirror/addon/search/search'
], function(CodeMirror) {
instanceName = CodeMirror.fromTextArea(document.getElementById(somefilename + 'xml'), {
lineNumbers: true,
mode: 'xml'
});
instanceName.setValue(xml);
instanceName.focus();
});
};
I actually was going about this the wrong way and should have been utilizing an object to store said multiple instances. Which of course gives me the ability to reference ANY stored records.
Hope this helps someone in the future!
//Create your object to store multiple instances of CodeMirror
var cmInstances = {}
function createInstance(someId) {
In my case I will be using requirejs to load 'on demand'
require([
'codemirror/lib/codemirror', 'codemirror/mode/xml/xml',
'codemirror/addon/search/search'
], function(CodeMirror) {
So what I'm doing below is creating a instance of the Codemirror object within my object. That essentially keys to the ID I've previously passed in the function (ie: 'someId). Also before I called the below I've had a previous function create a 'textarea' that I've injected into the page named '#someID_xml'. Hence the reference for getElementByID
cmInstances[someId] = CodeMirror.fromTextArea(document.getElementById(someId + '_xml'), {
lineNumbers: true,
mode: 'xml'
});
});
};
I've got a parent class ParentPanel that extends Ext.form.Panel in Ext JS 5. The parent panel is roughly defined as follows:
Ext.define('...ParentPanel', {
config : {
isItTrue : true
},
dockedItems: [],
initComponent : function () {
var me = this;
//If some evaluation is true...
if (isItTrue) {
me.dockedItems.push({
// the components I am trying to add...
});
this.callParent():
}
//....
The basic problem is that any class extending ParentPanel will call the initComponent function, adding the items to the parent dockedItems config. for every true evaluation (so I end up with repetitions as each child is sharing the parent's dockedItems config.). What I want to do is to only have the item added to the inheriting classes dockedItems configuration, not the parent.
Is this even possible is Ext JS? If so, understanding the design issue, how would I work around this?
Something like this:
initComponent: function () {
this.dockedItems = [];
// ...
}
ExtJS doesn't change the nature of Javascript...
I'm quite experienced with jQuery but I'm just getting into object oriented Javascript and want to make sure I'm doing it right.
Here's an example of what I'm doing so far... I'd appreciate tips/advice on how I can improve my approach:
// create a class for collapsable fieldsets
window.CollapsableFieldset = (function() {
function CollapsableFieldset(jQObject) {
// look for all the legend tags within this fieldset
jQObject.find("legend").click(function(event) {
// add or remove the "open" class to the fieldset
return jQObject.toggleClass("open");
});
}
return CollapsableFieldset;
})();
jQuery(document).ready(function() {
return $("fieldset.collapsable").each(function(i, element) {
return new CollapsableFieldset($(element));
});
});
So basically I'm creating a class that defines the behaviour I'm looking for and then in the jQuery block, I'm selecting each object that matches and creating a new instance of the class with that object as an argument.
This is not object-oriented javascript.
Before getting into prototyping, let me make one remark: your use of a closure in CollapsableFieldset is unnecessary and cumbersome. Indeed, you do not have any variables in the (function(){})() pattern. You may rewrite, without any loss, to:
window.CollapsableFieldset = function (jQObject) {
jQObject.find('legend').click(function(event) {
return jQObject.toggleClass('open');
});
}
Now, you create classes in javascript using a function constructor, in which the "this" keyword defines the object that a "new" statement will yield:
function CollapsableFieldset (jQObject) {
// This is the function constructor.
this.field = jQObject;
}
CollapsableFieldset.prototype.findAndToggle = function (tag, cssclass) {
// This defines a method "findAndToggle" on the CollapsableFieldset type.
this.field.find(tag).click(function(event) {
return this.field.toggleClass(cssclass);
});
};
jQuery(document).ready(function() {
return $("fieldset.collapsable").each(function(i, element) {
var colFieldset = new CollapsableFieldset($(element));
return colFieldset.findAndToggle('legend', 'open');
});
});
Please bear in mind that object-oriented javascript is not always the optimal solution.
One option would be to use Jquery UI's widget factory and encapsulate behaviour in widgets.
http://wiki.jqueryui.com/w/page/12138135/Widget-factory
You are using an immediately called function expression for no good reason, your code is functionally equivalent to:
function CollapsableFieldset(jQObject) {
// look for all the legend tags within this fieldset
jQObject.find("legend").click(function(event) {
// add or remove the "open" class to the fieldset
return jQObject.toggleClass("open");
});
}
The "constructor" doesn't return the object created when it is called with new, no advantage it taken of prototype inheritance so no point to using new.
The object returned by the function isn't assigned to anything, so what's the point of returning anything?
> jQuery(document).ready(function() {
> return $("fieldset.collapsable").each(function(i, element) {
> return new CollapsableFieldset($(element));
> });
> });
Where does the object returned by new CollapsableFieldset(...) go?
Incidentally, there is no point to creating global variables using:
window.varName...
when
var varName ...
does it so much better.
I have a class - call it ThankYou that delivers these. Based on the different implementations, generic or Facebook, I need to deliver customized layout. Right now, I am building the HTML in JS and delivering the layout.
1) Sign up for email newsletters (for generic and Facebook implementation)
2) Teaser content (for generic implementation)
3) Facebook like(for Facebook ThankYou implementation)
Which design pattern do you think this is better implemented using - Factory or Mediator? I am just starting to utilize some design patterns in my code and would like to start off on the right foot.
Some notes:
a) Although the functionality might be the same, the layout might be different for generic and Facebook
If I don't use a design pattern, I could easily do this using an 'if' statement, but I am just looking for a more elegant solution.
I think that Factory more suitable in this case. You have base class (interface) called IThankYou that implements common methods and two classes extending base functionality. Factory stores mapping between type and class.
Small example code:
function IThankYou () {}
IThankYou.prototype = {
templates: { // this is common field for all instances extending this class
like: '<div class="b-like">Like</div>',
},
like: function () { throw "Unimplemented" }, // this method is not implemented in base class
commonMethod: function () { } // this is common method
};
function GenericThankYou (someParam) { this.someParam = someParam; };
GenericThankYou.prototype = new IThankYou;
GenericThankYou.prototype.like = function () {
// there you could use base class fields and methods
this.commonMethod();
};
function FacebookThankYou (someParam) { this.someParam = someParam; };
FacebookThankYou.prototype = new IThankYou;
FacebookThankYou.prototype.like = function () {
// there you could use base class templates map
};
var ThankYouFactory = {
typeMap: {
'facebook' : FacebookThankYou,
'generic' : GenericThankYou
},
getConstructor: function (type) {
return this.typeMap[type];
}
};
ThankYouFactory.getConstructor('facebook')(ctorParam);
ThankYouFactory.getConstructor('generic')(ctorParam);