How to organize EmberJS nested resources? - 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.

Related

Changing anyMatch default for Filter.JS in ExtJS for MultiSelect search

I have a multiselect bound to a store in which I implemented use of anyMatch: true to allow for True to allow any match - no regex start/end line anchors will be added (as per the comment in Filter.js). My problem is that I need to implement this as per the answer to multiselect-search-whole-string, in particular the solution provided in this fiddle https://fiddle.sencha.com/#fiddle/jf5
What I want to do is just set anyMatch: true, regardless, so I set it in Filter.js, but this has no effect on use of it. I searched the entire codebase for other instances of anyMatch: false and the only other one is in ext-all-debug.js. Why isn't setting these values having any effect? I don't see where else this default value could be set?
EDIT 1
I tried a different override, and while it is not exhibiting the right behavior, it is actually doing something this time. I figured that since the chunk of code that does work when embedded in the search attribute within the MultiSelector control was pretty much what was found in the MultiSelectorSearch's search method, that this was what I needed to focus on for the override. Any suggestions on tweaking this would be most welcome:
Ext.define('Ext.overrides.view.MultiSelectorSearch', {
override: 'Ext.view.MultiSelectorSearch',
search: function (text, me) {
var filter = me.searchFilter,
filters = me.getSearchStore().getFilters();
if (text) {
filters.beginUpdate();
if (filter) {
filter.setValue(text);
} else {
me.searchFilter = filter = new Ext.util.Filter({
id: 'search',
property: me.field,
value: text,
anyMatch: true
});
}
filters.add(filter);
filters.endUpdate();
} else if (filter) {
filters.remove(filter);
}
}
});
EDIT 2
Got it! The key was that originally, since this code was embedded in a singleton, I could reference the method by passing me from the calling form.panel. This did not work globally as an override, and required me to define the method as
search: function (text) {
var me = this,
I hope this helps someone out there!
Changing in ext-all-debug.js is not safe, when you do a production build this file will not get included.
Best way is to override the Filter class, here is how you can do it.
Ext.define('Ext.overrides.util.Filter', {
override: 'Ext.util.Filter',
anyMatch: true
});
And import this class in Application.js
Ext.require([
'Ext.overrides.util.Filter'
]);

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.

Require.js best practise when loading different scripts depending on local conditions

For the sake of argument, say I want to load Zepto by default, but use jQuery instead for IE (all versions).
What would be a sensible way to do this when using Require.js?
There are two approaches: "proper" but long and "sleight of hand" but short.
"Proper" but long:
require.config({
paths: {
jquery:'path/to/jquery'
, zepto: 'path/to/zepto'
}
})
var iNeed = []
if (!('__proto__' in {})) {
// This is IE
iNeed.push('jquery')
} else {
// Everything else
iNeed.push('zepto')
}
require(iNeed, callback)
"Sleight of hand" but short:
var AMDConfig = {
paths: {
jquery:'path/to/zepto'
}
}
if (!('__proto__' in {})) {
// This is IE
AMDConfig.paths.jquery = 'path/to/jquery'
}
require.config(AMDConfig)
require(['jquery'], callback)
The reason the "sleight of hand" is not "proper" is that you are masking the real nature of what stands behind "jquery" As you grow your app, some jQuery plugins may come and not work over zepto, but it will not be immediately clear what the issue is.
The "proper" solution is also a problem in one respect - if you define the requirements array dynamically, the build tool like r.js will not be able to find other dependencies you put there.
Your pick..

Can I add attributes to a Backbone View?

I have been working with backbone for a while and I am now using a number of views. In some of my views I sometimes add custom attributes like:
var DataGrid = Backbone.View.extend({
className:"datagrid",
lookup: {
header: "", //Header wrapper row element
headers: [], //Views in header
body: "", //Body wrapper row element
rows: [] //Views in body
},
events: {
...
},
initialize: function() {
...
},
render: function() {
...
}
});
As you can see I have "lookup" as an extra attribute to the Object. I use DataGrid in a number of my views and I am experiencing a very strange behaviour. When I switch between views that use DataGrid, "lookup" would still be populated with the old data. I use "new" when creating a new DataGrid but I still find old data. Am I missing something?
EDIT: Following #rabs reply. I did a search on static variables in Backbone and found this: Simplify using static class properties in Backbone.js with Coffeescript
I know an answer has been accepted on this (a while ago), but as I came across this question while working on a backbone project recently, I thought it would be worth mentioning that you can define attributes as a function also. This is especially useful for views that need to have attributes set to values in their current models.
By defining attributes as a function you can do something like
var myObject = Backbone.View.extends({
attributes: function() {
if(this.model) {
return {
value: this.model.get('age')
}
}
return {}
}
});
Hope that helps someone
Declaring variables in this way the scope of the variable is to the class not the instance, similar to s static or class variable.
So yeah the lookup object will shared between your different instances.
You could pass the lookup object in to your instance when you create it that way it will behave as an instance variable.

Extending the YUI Panel

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>');
});
}
});

Resources