Can I add attributes to a Backbone View? - object

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.

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

Statically load a QML Component without instantiating it

As I understand it, a QML Component is like a kind of like a class in C++. It contains the definition of a QML object but isn't an instance of it. You can create a Component in these ways:
Creating a .qml file with the component name as its filename.
Define it inline with the Component { } syntax.
However these are actually two different things. The second one is more like a factory because you can do things like:
Component {
id: factory
Rectangle { width: 100; height:100; color: "red }
}
Component.onCompleted: {
var rect1 = factory.createObject(parent);
}
Whereas with the separate file you need to first load it into a factory like this:
var factory = Qt.createComponent("RedRectangle.qml")
var rect1 = factory.createObject(parent);
I'm only concerned with dynamic object creation, so this is not an option:
RedRectangle {
id: rect1
}
My question is: is there a way to create the objects dynamically, without having to create the Component factory dynamically too, and without having to specify the Component inline. I.e. I want the first example, but where the Rectangle is specified in another file.
I want this:
Component {
id: factory
url: "RedRectangle.qml"
}
Component.onCompleted: {
var rect1 = factory.createObject(parent);
}
Sadly that doesn't work. I also tried this:
Component {
id: factory
}
Component.onCompleted: factory.loadUrl("RedRectangle.qml");
But it doesn't work either. Am I being stupid or is this just not supported?
Here is some encapsulation:
Fact.qml (for some reason it doesn't let me name it Factory)
QtObject {
property string url
readonly property Component component : Qt.createComponent(url)
function get() { return component }
function load(url) { return Qt.createComponent(url) }
}
usage:
Fact {
id: f
url: "RedRect.qml"
}
StackView {
id: stack
}
Component.onCompleted: {
stack.push(f.component) // redrect
f.url = "BlueRect.qml"
stack.push(f.get()) // bluerect, redundant but shorter
stack.push(f.load("GreenRect.qml")) // greenrect, f.component is still bluerect
}
It will only load the component when its component property is referenced and you can change the url to load other components with the same Fact instance. Also the auxiliary load() method, which returns a component without actually changing the one potentially cached.
Actually the answer is not too bad, though I still think Component should support specifying a url directly.
Here is my solution:
property var factory: Qt.createComponent("RedRectangle.qml")

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.

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