Extending the YUI Panel - yui

I have a requirement to extend the YUI Panel with some custom functionality that will be in a new file and shared across multiple views.
I am at a bit of a loss as to how best to go about this, can anyone give me any pointers please?

Let's say you want to extend a Panel to create one that has a list in its body. I usually use Y.Base.create for this. It's a more declarative way of extending YUI classes than using a constructor and Y.extend. But I'll stay closer to your example in the YUI forums.
There are a couple of tricks dealing with WidgetStdMod (one of the components of Y.Panel), but mostly it's just about using Y.extend and following the YUI inheritance patterns. I'll try to answer with an example:
function MyPanel() {
MyPanel.superclass.constructor.apply(this, arguments);
}
// hack: call it the same so you get the same css class names
// this is good for demos and tests. probably not for real life
MyPanel.NAME = 'panel';
MyPanel.ATTRS = {
listItems: {
// YUI now clones this array, so all's right with the world
value: []
},
bodyContent: {
// we want this so that WidgetStdMod creates the body node
// and we can insert our list inside it
value: ''
}
};
Y.extend(MyPanel, Y.Panel, {
// always a nice idea to keep templates in the prototype
LIST_TEMPLATE: '<ul class="yui3-panel-list"></ul>',
initializer: function (config) {
// you'll probably want to use progressive enhancement here
this._listContainer = Y.Node.create(this.LIST_TEMPLATE);
// initializer is also the place where you'll want to instantiate other
// objects that will live inside the panel
},
renderUI: function () {
// you're inheriting from Panel, so you'll want to keep its rendering logic
// renderUI/bindUI/syncUI don't call the superclass automatically like
// initializer and destructor
MyPanel.superclass.renderUI.call(this);
// Normally we would append stuff to the body in the renderUI method
// Unfortunately, as of 3.5.0 YUI still removes all content from the body
// during renderUI, so we either hack it or do everything in syncUI
// Hacking WidgetStdModNode is doable but I don't have the code around
// and I haven't memorized it
//var body = this.getStdModNode('body');
},
syncUI: function () {
// same here
MyPanel.superclass.syncUI.call(this);
// insert stuff in the body node
var listContainer = this._listContainer.appendTo(this.getStdModNode('body'));
Y.Array.each(this.get('listItems'), function (item) {
listContainer.append('<li>' + item + '</li>');
});
}
});

Related

Extend a view or use a mixin in Ember.js

I would like to include standard functionality in several views within an Ember app. The functionality includes things like setting the tagName and classNames of the views to be the same and keeping track of a property for each view.
The question in a nutshell: Should I use a mixin or extend a base view?
The expanded question...
Should one extend a base view to do this? For example:
App.BaseView = Em.View.extend({
tagName: 'section',
classNames: ['page_section', 'blue'],
willInsertElement: function() {
// Some functions called here that set properties
},
});
App.PageOneView = App.BaseView.extend({
// View specific stuff here
});
App.PageTwoView = App.BaseView.extend({
// View specific stuff here
});
... Or, should one use a Mixin to extend the functionality? For example:
App.BaseMixin = Em.Mixin.create({
tagName: 'section',
classNames: ['page_section', 'blue'],
willInsertElement: function() {
// Some functions called here that set properties
},
});
App.PageOneView = Em.View.extend(App.BaseMixin, {
// View specific stuff here
});
App.PageTwoView = Em.View.extend(App.BaseMixin, {
// View specific stuff here
});
I understand that views and mixins are both Ember objects, but does using either of them to extend standard functionality to other objects (e.g. views) affects how the objects and prototypes/instances (if they differ from the object) interact and whether properties get set on the instance of the view or the view object?
If the two examples above differ, would setting the properties on the mixin's init function change anything? For example:
App.BaseMixin = Em.Mixin.create({
tagName: null,
classNames: null,
init: function() {
this.set('tagName', 'section');
// And so forth...
},
});
However, if using a mixin and extending a view have the same affect on the views I am trying to add the standard functionality to (that is, they affect the views' objects and prototypes/instances in the same way), do you see an advantage to using one over the other (whether in terms of efficiency, maintainability, etc)?
Great question,
Short and simple, extend the view.
The hooks/bindings are view specific, so the mixin can't be applied to a controller, route etc, and depending on your team makeup, you don't want to give someone an opportunity to mix in code that doesn't belong.
Extending a class in Ember just makes the base class into a mixin and applies it to your class. https://github.com/emberjs/ember.js/blob/v1.2.0/packages/ember-runtime/lib/system/core_object.js#L488
So it's almost the exact same thing, only your base view makes more sense since it only really applies to views.

How to organize EmberJS nested resources?

I'm trying to create a small EmberJS application, but I'm struggling about how to architecture it correctly. I have a main view called "library" which displays on a sidebar a list of folders. User can click on each folder and display the content at the center (while the sidebar is still active).
I therefore have a library resource, and nested resources to display the folders in this specific context:
this.resource('library', function() {
this.resource('libraryFolders', {path: 'folders'}, function() {
this.resource('libraryFolder', {path: ':folder_id'};
}
};
To be able to access the folders in the parent root, I set up a dependency:
App.LibraryController = Ember.Controller.extend({
needs: ["libraryFolders"],
folders: null,
foldersBinding: "controllers.libraryFolders"
});
App.LibraryRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('controllers.libraryFolders.model', App.Folder.find());
}
});
First question: is this a good way? I feel it a bit strange that a parent controller have a dependency to its children.
Now, another problem arises: what if I want to reuse folders in another context? All the methods I would write in LibraryFoldersController would be specific to this one, not really DRY. What I came up is adding a root "folders" resource, and add the dependency to this one instead:
this.resources('folders');
App.LibraryController = Ember.Controller.extend({
needs: ["Folders"],
folders: null,
foldersBinding: "controllers.folders"
});
App.LibraryRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('controllers.folders.model', App.Folder.find());
}
});
What do you think? Am I doing it wrong?
IMO it looks good so far. You are using the needs API which is the correct (ember) way to setup dependencies between controllers.
Maybe if you find yourself writing repeating code you could consider creating a Mixin for a more general controller an put there your logic, that should be agnostic to the use cases it handles.
For example defined a mixin:
App.ControllerMixin = Ember.Mixin.create({
// "use case" agnostic logic here
});
You mix mixins into classes by passing them as the first arguments to .extend.
App.LibraryController = Ember.ObjectController.extend(App.ControllerMixin, {
// now you can use here the logic defined in your mixin
// and add custom code as you please
});
Another possibility is to write a super class and then extend from it to inherit common logic:
Snippet taken from the docs:
App.Person = Ember.Object.extend({
helloWorld: function() {
alert("Hi, my name is " + this.get('name'));
}
});
var tom = App.Person.create({
name: 'Tom Dale'
});
tom.helloWorld(); // alerts "Hi, my name is Tom Dale".
One thing worth mentioning (though I think it's simply a typo) is: needs: ["Folders"] should be needs: ["folders"],
Hope it helps.

How can I break this code up into two RequireJs modules

I've created the code below to dynamically load 2 buttons into an element with an ID of masthead. Then a function called showMenus runs when each button is clicked, running some jQuery animations. Everything is wrapped inside of a RequireJS module.
The code works fine as is but I'm thinking it may be better to break it up into two separate RequireJS modules/files: one that loads the buttons on the page and another one that runs the showMenus function. I did refer to the RequireJS API docs but couldn't find an answer.
Any help is appreciated...thanks in advance!
require(['jquery'], function ($) {
var header = document.getElementById("masthead"),
$navMenu = $("#site-navigation-list"),
$searchBox = $("#searchform"),
menuButton = document.createElement("div"),
searchButton = document.createElement("div"),
showMenus;
$(menuButton).attr("id", "menu");
$(searchButton).attr("id", "search");
header.appendChild(searchButton);
header.appendChild(menuButton);
// break the code below into its on RequireJS module?
showMenus = function(btn,el) {
$(btn).click(function() {
if (el.is(":visible") ) {
el.slideUp({
complete:function(){
$(this).css("display","");
}
});
} else {
el.slideDown();
}
});
};
showMenus(menuButton, $navMenu);
showMenus(searchButton, $searchBox);
});
What follows is only my opinion, but you might find it useful.
It might help to think in terms of things that your app is made of, and then maybe they are candidates for modules. So in your example, a 'masthead' seems to be a thing that you are interested in.
So using RequireJS, we can create a new module representing a generic masthead:
// Masthead module
define(['jquery'], function ($) {
function showMenus (btn, el) {
function toggle (el) {
if (el.is(":visible")) {
el.slideUp({
complete:function(){
$(this).css("display","");
}
});
} else {
el.slideDown();
}
}
$(btn).click(function() {
toggle(el);
});
}
// A Masthead is an object that encapsulates a masthead DOM element.
// This is a constructor function.
function Masthead (mastheadElement) {
// 'this' is the masthead object that is created with the 'new'
// keyword in your application code.
// We save a reference to the jQuerified version of mastheadElement.
// So mastheadElement can be a DOM object or a CSS selector.
this.$mastheadElement = $(mastheadElement);
}
// Add a method to Masthead that creates a normal button
Masthead.prototype.addButton = function (id) {
var $btn = $("<div/>").attr("id", id);
this.$mastheadElement.append($btn);
return $btn;
};
// Add a method to Masthead that creates a 'toggling' button
Masthead.prototype.addTogglingButton = function (id, elementToToggle) {
// ensure we have a jQuerified version of element
elementToToggle = $(elementToToggle);
// Reuse the existing 'addButton' method of Masthead.
var $btn = this.addButton(id);
showMenus($btn, elementToToggle);
return $btn;
};
// return the Masthead constructor function as the module's return value.
return Masthead;
});
And then use this module in our actual application code:
// Application code using Masthead module
require(["Masthead"], function (Masthead) {
// We create a new Masthead around an existing DOM element
var masthead = new Masthead("#masthead");
// We add our buttons.
masthead.addTogglingButton("menu", "#site-navigation-list");
masthead.addTogglingButton("search", "#searchform");
});
The advantage of this approach is that no DOM ids are hard-coded into the module. So we can reuse the Masthead module in other applications that require this functionality, but which may be using different DOM ids.
It might be convenient to think of this as separating the what things are from the how we use them.
This is a simple example, but frameworks/libraries like Backbone and Dojo (and many, many more) take this further.

How to add context helper to Dust.js base on server and client

I'm working with Dust.js and Node/Express. Dust.js documents the context helpers functions, where the helper is embedded in the model data as a function. I am adding such a function in my JSON data model at the server, but the JSON response to the browser doesn't have the function property (i.e. from the below model, prop1 and prop2 are returned but the helper property is not.
/* JSON data */
model: {
prop1: "somestring",
prop2: "someotherstring",
helper: function (chunk, context, bodies) {
/* I help, then return a chunk */
}
/* more JSON data */
I see that JSON.stringify (called from response.json()) is removing the function property. Not sure I can avoid using JSON.stringify so will need an alternative method of sharing this helper function between server/client. There probably is a way to add the helper functions to the dust base on both server and client. That's what I'm looking for. Since the Dust docs are sparse, this is not documented. Also, I can't find any code snippets that demonstrate this.
Thanks for any help.
send your helpers in a separate file - define them in a base context in dust like so:
base = dust.makeBase({foo:function(){ code goes here }})
then everytime you call your templates, do something like this:
dust.render("index", base.push({baz: "bar"}), function(err, out) {
console.log(out);
});
what this basically does is it merges your template's context into base, which is like the 'global' context. don't worry too much about mucking up base if you push too much - everytime you push, base recreates a new context with the context you supplied AND the global context - the helpers and whatever variables you defined when you called makeBase.
hope this helps
If you want stringify to preserve functions you can use the following code.
JSON.stringify(model, function (key, value) {
if (typeof(value) === 'function') {
return value.toString();
} else {
return value;
}
});
This probably doesn't do what you want though. You most likely need to redefine the function on the client or use a technology like nowjs.

Multiple Greasemonkey Metablocks

I'm trying to write a Greasemonkey script for a hierarchy of websites such that I have a bunch of code modifications for http://www.foo.com/*, then more specific ones for http://www.foo.com/bar/*, and still others for http://www.foo.com/foobar/*.
Is there anyway for me to write all these in the same script, or do I have to make multiple?
Is there anyway for me to write all
these in the same script, or do I have
to make multiple?
Yes, just use those three #includes, then in your user script do something like (depends on specifics of script):
var currentURL = (document.location+'');
if (currentURL .match(/http:\/\/www\.foo\.com\/foobar\/.*/)) {
// do stuff for page set A
} else if (currentURL .match(/http:\/\/www\.foo\.com\/foo\/.*/)) {
// do stuff for page set B
} else if (currentURL .match(/http:\/\/www\.foo\.com\/.*/)) {
// do stuff for page set C
}
One nifty trick I was shown for dealing with different functions at different sub-locations is to use the global directory of function names as a sort of virtual switchboard...
// do anything that is supposed to apply to the entire website above here.
var place = location.pathname.replace(/\/|\.(php|html)$/gi, "").toLowerCase();
// the regex converts from "foo/" or "foo.php" or "foo.html" to just "foo".
var handler;
if ((handler = global["at_" + place])) {
handler();
}
// end of top-level code. Following is all function definitions:
function at_foo() {
// do foo-based stuff here
}
function at_foobar() {
// do foobar stuff here.
}

Resources