Ember Controllers and "virtual" and/or "abstract" methods? - javascript

I am aware that JS isn't designed to handle inheritance per OOP "cleanly", but I wonder if Ember has a way to pull this off.
In Ember, I figure that Ember.ObjectController.extend({...}); is somewhat inheritance, but not completely - we surely can add our own properties and methods, hence the function .extend({...}), but we can't "override" a function that already exists. I wonder if there is such a workaround to this.
If I created a base controller, how would I define functions that I want child controllers to implement? I have the base controller (theory code only):
App.BaseController = Ember.ObjectController.extend({
// Methods meant for a child controller to implement
abstractMethod1: null,
abstractMethod2: null,
virtualMethod1: function(){
...
},
actions: {
execute: function(){
if(this.get("abstractMethod1"))
this.get("abstractMethod1")();
...
if(this.get("abstractMethod2")
var x = this.get("abstractMethod2")();
}
}
});
Then I have an implementing controller trying to override those functions:
App.ImplementingController = App.BaseController.extend({
/* How would I implement abstractMethod1 and abstractMethod2 here!?
For virtualMethod1, how would I call something like base.virtualMethod1()
or super.virtualMethod1()?
*/
});
I found myself creating a ton of controllers that have basically the same code, except for the name of the model and its properties. It would be nice to be able to pull of this scheme in Ember. What to do?

Actually Ember does that perfectly fine, you just don't override it and it hits the base implementation. Or you do override it and it blasts away the base implementation. (this is essentially how Mixins work as well, http://emberjs.com/api/classes/Ember.Mixin.html) And if you want to hit a base function, property etc, it's just accessed with this (it essentially smashes the two classes together, giving precedence to the extended class.
Base
App.BaseController = Ember.ObjectController.extend({
a:'Base',
b:'Base',
acomp: function(){
return 'Base';
}.property(),
bcomp: function(){
return 'Base';
}.property(),
e:function(){
return 'Base';
},
f:function(){
return 'Base';
}
});
Extended
App.IndexController = App.BaseController.extend({
b:'Index',
c:'Index',
bcomp: function(){
return 'Index';
}.property(),
f:function(){
return 'Index';
},
actions:{
foo:function(){
console.log(this.e());
console.log(this.f());
}
}
});
What it looks like after Ember Combines them
App.IndexController....
a:'Base'
b:'Index',
c:'Index',
acomp: function(){
return 'Base';
}.property(),
bcomp: function(){
return 'Index';
}.property(),
e:function(){
return 'Base';
},
f:function(){
return 'Index';
},
actions:{
foo:function(){
console.log(this.e());
console.log(this.f());
}
}
});
http://emberjs.jsbin.com/wuhuleje/2/edit

Related

Durandal: Defining the same viewmodel (control) as two different variables

So I've created a scroller control that I want to use in two different places within the same viewmodel for example:-
define(['common/viewmodels/controls/scroller-nav', 'common/viewmodels/controls/scroller-nav'],
function(mainScrollNav, modalScrollNav))
vm = {
activate: activate,
mainScrollControl: ko.observable(null),
modalScrollControl : ko.observable(null)
}
return vm;
function activate() {
vm.mainScrollControl({ model: mainScrollNav, view: 'common/views/controls/mainScroll' });
vm.modalScrollControl({ model: modalScrollNav, view: 'common/views/controls/modalScroll' });
// load up the data that is to be used for each (should be independent)
mainScrollNav.init();
modalScrollNav.init();
}
}
}
The control loads fine on both instances where mainScrollControl and modalScrollControl is populated however, the data is being shared (modify the scroller position in the modal and its also modified on the main page) even though the controls are defined separately. It seems like mainScrollNav and modalScrollNav link to a single service viewmodel as opposed to being independent viewmodels. Am I going about this right way or should I be using something else?
The solution was not to create a viewmodel, but a function of the view model so...
var control = function(){
vm = {
// Vars and functions
}
return vm;
}
return control;
Then the viewmodel can be resused as many times as needed just by calling the passed reference in the define paramaters. They both work independently as well.
define(['common/viewmodels/controls/scroller-nav'],function(scrollNav)){
vm = {
mainScroller: new scrollNav(),
subPageScroller: new scrollNav()
}
return vm;

AngularJS How To Achieve Polymorphism / Dependency Injection (Best Practices)

This is a design pattern related question. I am not looking for an answer as to how to achieve the following, but rather the most widely accepted and way to achieve polymorphism in a service.
Suppose I had a service called getData. It needs to get some data, whether it be from a database, text file, or something hardcoded, and output it depending on what the settings are on the $scope in the controller. In this example below, suppose getData depends on the dataSource.
angular.module('testApp').controller('testController'), [$scope, myAwesomeService, function ($scope, myAwesomeService){
$scope.dataSource = 'database'; //defines the source of the data
$scope.getData = function() {
//use myAwesomeService, get the data and output
if($scope.dataSource ==='database') {
return //do it the first way
}
else if($scope.dataSource ==='text') {
return //do it the second way
}
else if($scope.dataSource ==='csvfile') {
return //do it the third way
}
else if($scope.dataSource ==='form') {
return //do it the fourth way
}
}
}]);
Questions:
How would you achieve this generally in Javascript? I am not sure about the best practices around achieving polymorphism in Javascript. I am used to using interfaces and dealing with the situation above by using dependency injection and passing in objects that adhere to the same interface, and call a common method, from the controller. Usually some other "class" would take care of selecting which object to instantiate and pass in, and therefore make the controller agnostic to concrete details as to "how it is done".
How would one go about doing this in AngularJS?
How would the pattern typically look? Can you give a "textbook" Angular way of achieving polymorphism?
I wanted to comment, but I realized it might be too long, so I'm going to post an answer.
If we are talking about ES5, polymorphism & inheritance can be achieved through prototyping.
For example:
function Auto(name,year){
this.year=year;
this.name=name;
}
Auto.prototype.showYear = function(){
console.log(this.year);
}
function Car(name,year, model){
Auto.call(this,name,year);
this.model=model;
}
Car.prototype = Object.create(Auto.prototype);
//usage
var car = new Car('BMW',2015,'320d');
car.showYear(); // will output 2015
In ES6 this can be done using class functions. You can read more about this, HERE ( it's gonna be very nice :D )
Below you'll find some code that might answer your question. Hope this is what you're looking for:
function BaseService(){
this.dataSource='database';
}
BaseService.prototype.getData = function(){
console.log('base: get data');
}
function TextService(){
this.dataSource='text';
}
TextService.prototype = new BaseService();
TextService.prototype.getData = function(){
console.log('child text: get data');
}
function CsvService(){
this.dataSource='csv';
}
CsvService.prototype = new BaseService();
CsvService.prototype.getData = function(){
console.log('child csv: get data');
}
function FormService(){
this.dataSource='form';
}
FormService.prototype = new BaseService();
FormService.prototype.getData = function(){
console.log('child form: get data');
}
angular.module('myApp').factory('awesomeService', function(){
var service={};
service.getData = function(dataSource){
var sourceService;
if(dataSource='database'){
sourceService= new BaseService();
}
if(dataSource=='text'){
sourceService=new TextService();
}
if(dataSource=='csv'){
sourceService = new CsvService();
}
if(dataSource=='form'){
sourceService= new FormService();
}
return sourceService.getData(); // i'm going to assume getData returns a promise
}
return service;
});
angular.module('myApp').controller('myController', function($scope,awesomeService){
var myDataSource='database';
$scope.getData = function(){
awesomeService.getData(myDataSource).then(function(data){
$scope.result=data;
});
}
});

Ember not rendering component at first

I'm building an Ember app which uses quite a few components. I'm also using Bootstrap. I've got a layout with tabs, and inside the second tab (which is hidden by default), the component (which contains a list of models which have a hasMany relationship with the main model) won't render.
I think I tracked this down to Ember Data resolving after the view is rendered, because if I click on another model of the list, these relations will show up.
Some info and details:
I have two main models:
Image
Crop
An image can have many crops.
I have an Images/Index controller which has this function:
loadCrops: function() {
var self = this;
this.get('selectedImage').get('crops').then(function(crops) {
self.set('selectedImageCrops', crops);
});
}.on('model.isFulfilled')
I added this method because I tried to manually resolve the relationship and get the crops for the image loaded in a variable but I had no luck with this. I'm passing the variables like this:
{{image-crops image=selectedImage crops=selectedImageCrops}}
This is my Index route:
export default Ember.Route.extend({
model: function () {
return this.store.find('image');
},
setupController: function(controller, model, request) {
controller.set('model', model);
}
});
If anyone needs more details please, ask for them. Thank you all!
When you use function() {}.on() you are telling Ember to execute that function when an event occurs. model.isFulfilled isn't an event though but a property so you need to observe it instead and do a quick check within the method that it really has been fullfilled (so it won't trigger if the promise is restarted for example)
loadCrops: function() {
if(!this.get('model.isFulfilled')) {
return;
}
var self = this;
this.get('selectedImage').get('crops').then(function(crops) {
self.set('selectedImageCrops', crops);
});
}.observes('model.isFulfilled')
Also as a side note I would suggest that you use an ES6 arrow function (which retains the outer this) instead of using var self = this, it's make the code a bit nicer.
loadCrops: function() {
if(!this.get('model.isFulfilled')) {
return;
}
this.get('selectedImage').get('crops').then((crops) => {
this.set('selectedImageCrops', crops);
});
}.observes('model.isFulfilled')
Try changing to a computed property:
selectedImageCrops: function() {
return this.get('selectedImage.crops');
}.property('selectedImage')
What I did in the end was moving the code to load the crops to the route's model, and returning an Ember.RSVP.hash with both the images and the crops, and then assigning it to the controller:
export default Ember.Route.extend({
/**
* Returns an Image instances array
*
* #method model
* #return {Ember.RSVP.hash} Images & Crops
*/
model: function () {
return Ember.RSVP.hash({
images: this.store.find('image'),
crops: this.store.find('crop')
});
},
/**
* Takes care of setting the needed variables in the controller
*
* #method setupController
*/
setupController: function(controller, model, request) {
controller.set('model', model.images);
controller.set('crops', model.crops);
}
});
Then I added a helper function in the controller to get the current image's crops:
selectedImageCrops: function() {
return this.get('crops').filter((obj) => {
return obj.image === this.get('selectedImage');
})[0];
}.property("selectedImage")
Thanks to #Karl-Johan Sjögren for the tip on the arrow function!

Automatic _.bindAll() in backbone.js

Is there a way to automatically do an _.bindAll for a backbone.js object?
I was talking to someone a while ago and they said that there was, but I have no idea where to start looking.
Example:
var TheView = Backbone.View.extend({
initialize: function() {
// HOW CAN I AVOID HAVING TO DO THIS?---->
_.bindAll(this,'render','on_element_01_click', 'on_element_02_click');
},
events: {
'click #element_01': 'on_element_01_click',
'click #element_02': 'on_element_02_click',
},
render: function(){
return this;
},
on_element_01_click: function(){
},
on_element_02_click: function(){
}
}
Do this instead:
_.bindAll(this);
Will bind ALL functions in this view.
I've since learned of a easier technique if you want to build bindAll in to your views (which is handy for things like AJAX callback methods that aren't auto-bound the way event handlers are). Basically you just override the constructor to perform the auto-binding.
var BoundModel = Backbone.Model.extend({
constructor: function() {
Backbone.Model.apply(this, arguments);
if (this.boundMethods) {
_(this).bindAll.apply(this, this.boundMethods);
}
}
})
var SubclassOfBoundModel = Backbone.Model.extend({
boundMethods: ['handleFetchResponse'],
initialize: function () {
this.model.on('sync', this.handleFetchResponse);
}
handleFetchResponse: function() {
// this function is bound to the model instance
}
})
Of course if you just wanted to bind all your methods you could leave out the "boundMethods" part and just have:
constructor: function() {
Backbone.Model.apply(this, arguments);
_(this).bindAll();
}
I tried doing this myself and I was able to get it working with something like this:
function bindOnExtend(clazz) {
var originalExtend = clazz.extend;
clazz.extend = function() {
var newSubClass = originalExtend.apply(this, arguments);
var originalInitialize = newSubClass.prototype.initialize;
newSubClass.prototype.initialize = function() {
// The constructor will get broken by bindAll; preserve it so _super keeps working
var realConstructor = this.constructor;
_.bindAll(this);
this.constructor = realConstructor;
originalInitialize.apply(this, arguments);
};
return bindOnExtend(newSubClass);
};
return clazz;
}
var BoundModel = Backbone.Model.extend();
bindOnExtend(BoundModel);
var BoundView = Backbone.View.extend();
bindOnExtend(BoundView);
However, I wouldn't recommend it. Doing that will make closures for every single method on every single model/view/whatever you instantiate. Not only does that add a slight increase in overall memory usage, it also opens up the possibility of memory leaks if you're not careful. Furthermore, it makes your stacktraces several lines longer, as they have to wind through bindOnExtend.
In my experience, having to do "_.bindAll(this, ..." is worth the trouble because:
1) it makes my code more clear/obvious to anyone coming after me
2) it encourages me to qualify my bindAll, instead of just using the 1-arg form
3) I hate wading through long stacktraces
But, if you want it the above code should work.

jQuery Plugins: If I want to call something like $('selector').plugin.group.method(), how can I achieve this?

I have written some relatively simple jQuery plug-ins, but I am contemplating writing something more advanced in order to keep commonly used methods on the site easily accessible and DRY
For example, I might have something like this for a structure:
plugin
- popup
- element
...
=== popup ===
- login
- product
...
=== element ===
- shoppingCart
- loginStatus
...
So, to bind a popup login popup event, I'd like to be able to do:
$('#login_button').plugin.popup.login();
What's the best way to do this? Is there a better way of achieving what I want to do?
Cheers,
The way farhan Ahmad did it was pretty much right... it just needs deeper levels to suit your needs your implementation would look like this:
jQuery.fn.plugin = function(){
//"community" (global to local methods) vars here.
var selectedObjects = this; //-- save scope so you can use it later
// return the objects so you can call them as necessary
return {
popup: { //plugin.popup
login: function(){ //plugin.popup.login
//selectedObjects contains the original scope
console.log(selectedObjects);
},
product: function(){} //plugin.popup.product
},
element: { //plugin.element
shoppingCart: function() {}, //plugin.element.shoppingCart
loginStatus: function() {} //plugin.element.loginStatus
}
}
}
So now if you call:
$("#someDiv").plugin.login(); the result will be as expected. I hope this helps.
jQuery.fn.messagePlugin = function(){
var selectedObjects = this;
return {
saySomething : function(message){
$(selectedObjects).each(function(){
$(this).html(message);
});
return selectedObjects; // Preserve the jQuery chainability
},
anotherAction : function(){
//...
return selectedObjects;
}
};
}
We use it like this:
$('p').messagePlugin().saySomething('I am a Paragraph').css('color', 'red');
The selected objects are stored in the messagePlugin closure, and that function returns an object that contains the functions associated with the plugin, the in each function you can perform the desired actions to the currently selected objects.

Categories