I've a fair amount of experience with JavaScript, and for this new project I'm on (cms for blog with notions of profitability) I thought I'd step it up and write the JavaScript in an MVC fashion. I've been using a bit of backbone and underscore, but it isn't clicking mentally. any way, I've written a bit of code to handle some events/effects but it just doesn't work. If anyone could sort me out I'd really appreciate it.
// Semi Perfect grade 0 JS - Golden age
//partial View Objects | Events
var pshare_dock = {
actor: $("#share_dock"),
drag: function () {
this.actor.draggable();
}
}
pshare_dock.expand = function () {
this.actor.dblclick(function () {
$(this).toggleClass("share_close");
});
}
var pmenu = {
hover: function () {
$("ul.drop li.drop").hover(function () {
$(this).find('ul').fadeIn(1);
}, function () {
$(this).find('ul').hide();
})
},
navigate: function () {
$("a.ajx").click(function (e) {
var link;
var container = $("#content_pane");
e.preventDefault();
link = $(this).attr("href") + "#content_pane";
container.load(link);
})
}
}
var pcontent_pane = {}
var ppost = {}
var pdatabase_entry = {}
//Views
var Homepage = function () {
this.share_dock = function () {
new pshare_dock();
}
this.menu = function () {
new pmenu();
}
this.content_pane = function () {
new pcontent_pane();
}
this.posts = function () {
new ppost();
}
}
//Controller
var GoldenAgeRouter = Backbone.Router.extend({
routes: {
"!/": "defaultRoute",
"*actions": "defaultRoute"
},
defaultRoute: function (actions) {
var homeView = function () {
new Homepage();
}
}
})
$(document).ready(function () {
var Golden_age = function () {
new Homepage();
}
})
the question is essentially what all is wrong with this?
You're wrapping your instantiations in an anonymous function but not invoking them:
var Golden_age = new Homepage(); // Invoked.
var Golden_age = function(){ new Homepage(); } // Stored function, not invoked.
Related
I managed to recreate a MVC calorie tracker app from a course and I am trying to convert it to ES6 classes now.
I am a little stuck in understanding how to call the methods in the Module inside the Controller to return the items I need.
class Item {
constructor() {
this.data = {
items: [{
name: 'Salad',
calories: 200,
id: 0
},
{
name: 'Eggs',
calories: 500,
id: 1
}],
totalCalories: 0,
currentItem: null
}
};
getItems() {
return this.data.items
};
logData = () => {
console.log(data.items);
};
}
class App {
constructor(Item, UI) {
this.Item = Item;
this.UI = UI;
}
init() {
const items = Item.getItems();
UI.populateItemList(items)
}
}
const application = new App(new Item(), new UI())
When I try to call Item.logData() in the console it gives me TypeError: this.data is undefined.
I researched online and it seems that the method I declared is for the constructor only. How would I go about declaring methods that I'll use in the Controller or in any other class, just like I did below by returning a method out of the constructor?
What Im trying to convert initially looks like this:
const ItemCtrl = (function () {
const Item = function (id, name, calories) {
this.name = name;
this.id = id;
this.calories = calories;
}
const data = {
items: StorageCtrl.getStorage(),
totalCalories: 0,
currentItem: null
}
return {
getItems: function () {
return data.items
},
logData: function () {
return data;
}
}
const App = (function (ItemCtrl, StorageCtrl, UICtrl) {
return {
init: function () {
const items = ItemCtrl.getItems();
UICtrl.populateItems(items);
}
}
})(ItemCtrl, StorageCtrl, UICtrl);
App.init();
You need to initialise the controller first:
class App {
constructor(Item, UI) {
this.item = new Item();
this.UI = new UI();
}
init() {
const items = this.item.getItems();
this.UI.populateItemList(items)
}
}
I try the observer pattern (by these two Urls: https://davidwalsh.name/pubsub-javascript, http://www.dofactory.com/javascript/observer-design-pattern) but listeners array is empty when I call the publish function.
main.pagination.event= (function () {
var listeners = [];
return {
subscribe: function (fn) {
listeners.push(fn);
return {
unsubscribe: function (fn) {
listeners= listeners.filter(
function (item) {
if (item !== fn) {
return item;
}
}
);
}
};
},
publish: function () {
//it's empty
listeners.forEach(function (item) {
item("...");
});
}
};
})();
main.pagination.init = function () {
$('ul li').click(function () {
main.pagination.event.publish();
};
};
main.update.init = function() {
var event = main.pagination.event.subscribe(main.update.listener);
};
main.update.listener = function (tbl) {
alert(tbl);
};
Thanks for help.
It is empty because you call subscribe after publish which does not contain anything inside the listeners array. Just change the order of the calls like so
main.update.listener = function (tbl) {
alert(tbl);
};
main.pagination.init = function () {
$('ul li').click(function () {
main.pagination.event.publish();
};
};
main.update.init = function() {
var event = main.pagination.event.subscribe(main.update.listener);
};
main.update.init(); // invoke subscribe first to add the listener to the array
main.pagination.init();
I have some functions that are making some Facebook API calls, and an init one:
var fbAPI = {
init: function () {
this.friendList(), this.friendCount();
},
friendList: function (closure) {
var _self = this;
return FB.api(
"/me/taggable_friends",
function (data) {
_self.setFriends(data);
closure(data);
}
);
},
friendCount: function (closure) {
var _self = this;
return FB.api(
"/me/friends",
function (data) {
_self.setFriendsCount(data.summary.total_count);
closure(data);
}
);
},
friends: null,
friendsCount: null,
setFriends: function (data) {
this.friends = data;
},
setFriendsCount: function (count) {
this.friendsCount = count;
}
};
When all the calls are finished I want to remove the loading overlay.
Something like:
(fbAPI.init()).finish(function(){
//do something
});
How can I achieve this?
Here is a very basic attempt to create a "hello world"-like JS app using the module and MVC patterns.
var appModules = {};
appModules.exampleModul = (function () {
var _data = ['foo', 'bar']; // private variable
return {
view: {
display: function() {
$('body').append(appModules.exampleModul.model.getAsString());
},
},
model: {
getAsString: function() {
return _data.join(', ');
},
}
};
})();
appModules.exampleModul.view.display();
This works fine, but I'm not happy how I have to reference the model function from the view, using the full object path: appModules.exampleModul.model.getAsString(). How can I expose the public model methods to the view, so I could simply use something like model.getAsString()? Or do I need to organize the code differently?
One option is you can convert those objects into private implementations.
appModules.exampleModul = (function() {
var _data = ['foo', 'bar'];
// private variable
var _view = {
display : function() {
$('body').append(_model.getAsString());
},
};
var _model = {
getAsString : function() {
return _data.join(', ');
},
};
return {
view : _view,
model : _model
};
})();
You could do something like this:
var appModules = {};
appModules.exampleModul = (function () {
var _data = ['foo', 'bar']; // private variable
return {
view: {
display: function() {
$('body').append(this.model.getAsString());
},
},
model: {
getAsString: function() {
return _data.join(', ');
},
}
};
})();
var display = appModules.exampleModul.view.display.bind(appModules.exampleModul);
display();
Which isn't really the prettiest of solutions, but does offer a more generic solution inside the display function!
Is there any way I can do have a Javascript class that extends an object that was created through the revealing module pattern? I tried the following code, but is there away to achieve the same thing?
sv.MergeQuestionViewModel = function () {
this = sv.QuestionDetailViewModal();
this.init($("#mergeQuestionModel"));
};
sv.QuestionDetailViewModal = function () {
var $el,
self = this,
_question = ko.observable(),
_status = new sv.Status();
var _init = function (el) {
$el = el;
$el.modal({
show: false,
backdrop: "static"
});
};
var _show = function () {
$el.modal('show');
};
var _render = function (item) {
_question(new sv.QuestionViewModel(item));
_show();
};
var _reset = function () {
_question(null);
_status.clear();
};
var _close = function () {
$el.modal('hide');
_reset();
};
return {
init: _init,
show: _show,
render: _render,
reset: _reset,
close: _close
};
};
You could use jQuery.extend to achive this behaviour.
sv.MergeQuestionViewModel = function () {
$.extend(this, sv.QuestionDetailViewModal);
this.init($("#mergeQuestionModel"));
};
sv.QuestionDetailViewModal = (function () {
var el,
_init = function($el) {
el = $el;
console.log('init', el);
},
_render = function() {
console.log('render', el);
};
return {
init : _init,
render : _render
};
}());
var view = new sv.MergeQuestionViewModel();
view.render();
Test it on http://jsfiddle.net/GEGNM/