I am having trouble with some really basic Backbone.js functionality.
window.Album = new Backbone.Model.extend({});
album = Album({title:'OK Computer', artist:'Radiohead'});
album.isNew
function () {
return this.id == null;
}
album.toJSON
function () {
return _.clone(this.attributes);
}
.isNew, for instance, should just return 'yes'.
I'm using a gem "backbone-on-rails" if that makes any difference.
Any suggestions?
No man you don't have trouble with backbone, you have trouble with javascript ;)
In js, you need to call functions, i.e.
album.isNew();
album.toJSON();
Related
I searched several Angular style guides and best practices links, but I couldn't find a note on this issue. (Note, I am not using $scope.) If we have a ordinary web page with elements that invoke functions on the controller, my instinct from working with WPF was to do:
<button ng-click="myCtrl.save()">Save</button>
...
function myController(thingSavingService){
var ctrl = this;
// ...
ctrl.model.thing = {};
// ...
ctrl.saveThing() = function () {
thingSavingService.save(ctrl.model.thing);
}
}
Someone suggested that perhaps having element-specific handler functions is a good practice, like:
<button ng-click="myCtrl.onSaveClicked()">Save</button>
...
function myController(thingSavingService){
var ctrl = this;
// ...
ctrl.model.thing = {};
// ...
ctrl.onSaveClicked = function () {
saveThing();
}
var saveThing = function () {
thingSavingService.save(ctrl.model.thing);
}
}
Is binding these things directly to the function on the controller a good practice, or is it better to have another layer in between so that "action handlers" are separate from the domain-level functions?
In other words, with Angular, should I bind to "domain command" functions (less abstraction) or GUI action handling functions (more abstraction)?
Also, are there any articles or best practice guides that explore these nuances?
Personally, I follow John Papa's styleguide as for functions placing and abstraction level where he states to have pure functions below their assigning to controller. It's of course mainly for readability purposes.
function SessionsController() {
var vm = this;
vm.gotoSession = gotoSession;
vm.refresh = refresh;
vm.search = search;
vm.sessions = [];
vm.title = 'Sessions';
////////////
function gotoSession() {
/* */
}
function refresh() {
/* */
}
function search() {
/* */
}
}
https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md#style-y033
It is more about styling. In sitepoint there is this article StyleGuide which talks about styling guides.
As LIFT principle states that;
It is better do in this way
function myController(thingSavingService){
var ctrl = this;
ctrl.model.thing = {};
ctrl.onSaveClicked = saveThing;
var saveThing = function () {
thingSavingService.save(ctrl.model.thing);
}
}
Not because of performance, but because more readability.
As per your terminology, angular controller public method can be treated as "action handlers" and those are seperate from domain-level functions. When I say domain level functions, I am referring to Angular Services Functions/apis.
In my controller, I have a function defined as:
var ProductListingHeaderController = function (FilterService, CategoryService) {
this.isCategorySet = function () {
return (FilterService.categoryID);
};
this.categoryName = function () {
return (CategoryService.categoryName());
};
};
The IDE (via code highlighting) reports categoryName() as being used and isCategorySet() as unused.
This is kind of understandable, since:
categoryName() is used inside {{ }} in the html file:
<h2>{{productListingHeader.categoryName()}}</h2>
and isCategorySet() is used in an ng-if string:
ng-if="productListingHeader.isCategorySet()"
Given that this is such a common usage, I suspect I may be missing a setting in Storm as to how to set things up so that this type of usage (inside a string) by an Angular directive gets picked up as "used".
Thanks in advance for any feedback.
That's normal behavior for PHP/WebStorm.
Template -> JavaScript is really just an ambiguous connection. There is no support for jsDoc type inferring in the AngularJS templates. So PHP/WebStorm will match a template function call to JavaScript if only that function name is unique.
PHP/WebStorm have issues inferring closure methods as object functions. I've had better success using prototype declaration for AngularJS controllers.
var ProductListingHeaderController = function (FilterService, CategoryService) {
this.filterService = FilterService;
this.categoryService = CategoryService;
}
ProductListingHeaderController.prototype.isCategorySet = function () {
return (this.filterService.categoryID);
};
ProductListingHeaderController.prototype.categoryName = function () {
return (this.categoryService.categoryName());
};
Compare the above code with your code, and look at the structure explore in WebStorm. When you use prototypes for controllers they appear in the explorer properly.
First I'd like to say my appreciation for this great website that I rely on rather often but never have used to ask anything.
I'm currently learning AngularJS by reading "Mastering web application development with AngularJS" and going through the provided examples.
I would like to understand how I can switch between different implementations of a provider (service) with minimal code change.
Here is the "notifications" service that I need to configure with different implementations of an "archiver" service, code for both below :
angular.module('notificationsApp', ['archiver'])
.provider('notificationsService', function () {
var notifications = [];
return {
$get : function(archiverService) {
return {
push:function (notification) {
var notificationToArchive;
var newLen = notifications.unshift(notification);
if (newLen > 5) {
notificationToArchive = notifications.pop();
archiverService.archive(notificationToArchive);
}
}
};
}
};
})
.config(function(notificationsServiceProvider){
**How could I make the 'archiverService' be of superConsoleArchiverService or consoleArchiverService ?**
});
I would like to be able to choose between different implementations for my "archiverService", namely "superConsoleArchiverService" or "consoleArchiverService" as defined in the following module.
angular.module('archiver', [])
.provider('consoleArchiverService', function () {
return {
$get : function() {
return {
archive:function (archivedNotification) {
console.log(archivedNotification);
}
};
}
};
})
.provider('superConsoleArchiverService', function () {
return {
$get : function() {
return {
archive:function (archivedNotification) {
console.log('super ' + archivedNotification);
}
};
}
};
});
Thanks a lot for helping me through this !
(also, I hope this question makes sense and has not been answered a gazillion times)
Let's say you have some condition, say a variable to use_super.
Then you could do something like this, by injecting $provide, and both of your providers:
$provide.value('archiver', use_super ? superConsoleArchiverService : consoleArchiverService);
Hope this helped!
Thanks to the answer provided by hassassin I was able to make it work, following is some working version, no code was changed in the 'archiver' module.
angular.module('notificationsApp', ['archiver'])
.provider('notificationsService', function () {
var notifications = [];
return {
$get : function(configuredArchiver) {
return {
push:function (notification) {
var notificationToArchive;
var newLen = notifications.unshift(notification);
if (newLen > 5) {
notificationToArchive = notifications.pop();
configuredArchiver.archive(notificationToArchive);
}
}
};
}
};
})
.config(function(notificationsServiceProvider, superConsoleArchiverServiceProvider, consoleArchiverServiceProvider, $provide){
// Here it is possible to set the 'configuredArchiver to either one of my archivers
//$provide.provider('configuredArchiver',consoleArchiverServiceProvider);
$provide.provider('configuredArchiver',superConsoleArchiverServiceProvider);
});
Some things I still don't fully understand like why can't I inject the 'configuredArchiver' directly in the 'notificationService' provider, but I strongly suspect it is related to my still very small grasp on the life cycle of AngularJS objects. Back to reading !
I am using same el for more than 1 view like below. I'm not facing any problem till now. Is this good approach or should i do any changes?
<div id="app">
<div id="app-header"></div>
<div id="app-container"></div>
<div id="app-footer">
</div>
App View:
{
el: "#app",
v1: new View1(),
v2: new View2(),
render: function () {
if (cond1) {
this.v1.render();
} else if (cond2) {
this.v2.render();
}}
}
View 1:
{
el: "#app-container",
render: function (){
this.$el.html(template);
}
}
View 2:
{
el: "#app-container",
render: function (){
this.$el.html(template);
}
}
By reading your question, I do not really see what advantages you could possibly have using this approach rather than having the different div elements being the root el for your views 1, 2, 3 and using
this.$el.html(template)
in the render method.
Your approach could work for a small application, but I think it will become really hard to maintain as the application grows.
EDIT
I still do not really get your point, you could only initialize everything only once in both cases.
Here is a working Fiddle.
By the way I am changing the content by listening to the click event but this is to simplify the example. It should be done by the router.
I do use a mixin to handle such situation, I call it stated view. For a view with all other options I will send a parameter called 'state', render will in-turn call renderState first time and there after every time I set a 'state' renderState will update the view state. Here is my mixin code looks like.
var setupStateEvents = function (context) {
var stateConfigs = context.getOption('states');
if (!stateConfigs) {
return;
}
var state;
var statedView;
var cleanUpState = function () {
if (statedView) {
statedView.remove();
}
};
var renderState = function (StateView) {
statedView = util.createView({
View: StateView,
model: context.model,
parentEl: context.$('.state-view'),
parentView:context
});
};
context.setState = function (toState) {
if (typeof toState === 'string') {
if (state === toState) {
return;
}
state = toState;
var StateView = stateConfigs[toState];
if (StateView) {
cleanUpState();
renderState(StateView);
} else {
throw new Error('Invalid State');
}
} else {
throw new Error('state should be a string');
}
};
context.getState = function () {
return state;
};
context.removeReferences(function(){
stateConfigs = null;
state=null;
statedView=null;
context=null;
})
};
full code can be seen here
https://github.com/ravihamsa/baseapp/blob/master/js/base/view.js
hope this helps
Backbone Rule:
When you create an instance of a view, it'll bind all events to el if
it was assigned, else view creates and assigns an empty div as el for that view and bind
all events to that view.
In my case, if i assign #app-container to view 1 and view 2 as el and when i initialize both views like below in App View, all events bind to the same container (i.e #app-container)
this.v1 = new App.View1();
this.v2 = new App.View2();
Will it lead to any memory leaks / Zombies?
No way. No way. Because ultimately you are having only one instance for each view. So this won't cause any memory leaks.
Where does it become problematic?
When your app grows, it is very common to use same id for a tag in both views. For example, you may have button with an id btn-save in both view's template. So when you bind btn-save in both views and when you click button in any one the view, it will trigger both views save method.
See this jsFiddle. This'll explain this case.
Can i use same el for both view?
It is up to you. If you avoid binding events based on same id or class name in both views, you won't have any problem. But you can avoid using same id but it's so complex to avoid same class names in both views.
So for me, it looks #Daniel Perez answer is more promising. So i'm going to use his approach.
Usually I find my self needing to write an object with a specific functionality that it is a set of models.
Finally I extend a collection and add more functions that works with its model.
I think is better show you an example:
My app has a set of permissions, related with the user and/or the version of the platform.
var Permissions = Backbone.Collection.extend({
model: Permission,
hasAccess: function (moduleCode) {
....
},
allowAccess: function (moduleCode) {
....
},
...
With that methods I change the format of a property of a permission (the model). (My permissions are a concatenation of code with an string that identify the type of permission.)
A workmate tells me that it is wrong. In .NET he creates a class and he adds a private list and makes the changes to it. He does not inherit the list and changes it.
He would make a model and inside it he would add a collection property
this.set("permissionsCollection", new Backbone.Collection.extend({model: Permission}))
[Comment: I don't understand why he creates properties of everything, I think in his case it is not needed.] -> But this is another question
I think in a different way. I know the Collection has internally that list. I have a great potencial in Backbone.Collections, why do I have to use a model that it is not necessary? If I don't need that encapsulation... I think that it is not necessary, he is overprogramming in my opinnion.
Am I wrong? Did I not know how to use BackboneJS Collections?
Thank you in advance.
At the beginning I had something called helper with similar methods:
findAttr: function (model, name) {
var attrs = model.get('app_attrs');
if (attrs !== undefined) {
return this.findByAttrName(attrs, name);
}
},
findByAttrName: function (array, name) {
return _.find(array, function(a) {
if (a.attrName === name) {
return a;
}
});
}
The view code was more awkward:
render: function () {
var attr = helper.findAttr(this.model, 'user');
...
return this;
}
The only logical solution was to move these methods into the model (this.model in the above case). After refactoring I've got:
render: function () {
var attr = this.model.findAttr('user');
...
return this;
}
which is of course more readable than the previous solution.