Javascript simple MVC + module pattern implementation - javascript

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!

Related

Converting Calories Tracking application to ES6 Classes

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

javascript: extending methods between objects, like mixins

I want to share or reuse some logic between differents objects, that they will be pretty similar, just changing the "scope".
var Mixin = {
show: function () {
this.container.show();
},
hide: function () {
this.container.hide();
},
play: function (data) {
data.map().append();
}
};
var ObjectA = {
container: $('#container_a');
foo: function () {
this.play(otherData); // Mixin common method?
}
};
var ObjectB = {
container: $('#container_b'),
foo: function () {
this.play(data); // Mixin common method?
}
};
ObjectA.show() // show $('#container_a');
ObjectB.show() // show $('#container_b');
I was trying using underscore
_.extend(ObjectA, Mixin);
but it seems like I have issues with the reference of the Mixin (this reference to the last extended object), like if i need to clone the object and extend it?
Is there any approach to do something similar?
Thanks!!
EDIT: I having issue with the scope of 'this', that is referencing to window, when a pass as a callback a function inherits from the mixin, like this.
PersonMixin = {
mixinFoo: function () {
this.handleResponse();
}
};
Person = {
personMethod: function () {
OtherLibrary.libMehtod(this.mixinFoo);
}
};
Object.assign(Person, PersonMixin);
and then, something like this will fail, this an example stack trace
Person.personMethod();
OtherLibrary.libMethod(callbackMixin);
Ajax.post(callbackMixin);
callbackMixin(response); // this.handleResponse() is not defined, because this reference to window object.
EDIT 2: I can solve this issue using bind()
You can do this in a number of ways, my preference is adjusting the objects __proto__ property on creation which will cause it to inherit your mixin via its prototype chain. This does not require the use of underscore.
I adjusted your example for ES6 and made it a bit simpler but should get the point across.
const PlayerType = (
{ show() {
console.info(`show ${this.name}`)
}
, hide() {
console.info(`hide ${this.name}`)
}
, play: function (data) {
data.map().append();
}
}
)
const objA = { __proto__: PlayerType
, name: 'objA'
, foo(...args) {
this.play(...args)
}
}
const objB = { __proto__: PlayerType
, name: 'objB'
, foo(...args) {
this.play(...args)
}
}
objA.show()
objB.show()
Simpler and no ES6:
var Mixin = (
{ show() {
console.info('show ' + this.name)
}
, hide() {
console.info('hide ' + this.name)
}
}
)
var a = { __proto__: Mixin, name: 'a' }
var b = { __proto__: Mixin, name: 'b' }
a.show()
b.show()
Alternate - Does the same thing with Object.create().
var Mixin = (
{ show() {
console.info('show ' + this.name)
}
, hide() {
console.info('hide ' + this.name)
}
}
)
var a = Object.create(Mixin, { name: { value: 'a', enumerable: true } })
var b = Object.create(Mixin, { name: { value: 'b', enumerable: true } })
a.show()
b.show()
It works, just check your syntax also.
var Mixin = {
show: function() {
console.log(this.tmp);
}
}
var oA = {
tmp: 'tmpA'
}
var oB = {
tmp: 'tmpB'
}
var mA = Object.assign(oA, Mixin);
var mB = Object.assign(oB, Mixin)
mA.show();
mB.show()

Get name of property from its own function

I would like to get the name of a property from within its own function. My current approach does not work because the function is nameless. How can I do this?
window.APP = {
models: {
ex_model: kendo.observable({
ex_property: function () {
var property_name = arguments.callee.name.toString();
console.log(property_name);
},
}),
}
}
Thank you.
You can make ex_property have a name. Instead of using function(), you can say function function_name(), and then arguments.callee.name.toString() would return function_name. Like this:
window.APP = {
models: {
ex_model: kendo.observable({
ex_property: function function_name() {
var property_name = arguments.callee.name.toString();
console.log(property_name); // will return function_name
},
}),
}
}

(Partial) Extending "functions" in javascript?

I have this code...
var my = {
helpers: {
getName: function() {
return 'John Doe';
}
}
}
// in another file...
var my = {
helpers: {
getAge: function() {
return '40';
}
}
}
// Test...
$("#myDiv").html(my.helpers.getName + " " + my.helpers.getAge);
http://jsfiddle.net/MojoDK/8cmV7/
... but getName is undefined.
I was hoping javascript was smart enough to merge it into this...
var my = {
helpers: {
getName: function() {
return 'John Doe';
},
getAge: function() {
return '40';
}
}
}
How do I extend a method (or what it's called) like above? I have several "helper" files, that needs to "merge".
Redundancy is good for this:
my = window.my || {};
my.helpers = my.helpers || {};
my.helpers.getAge = function() {
return 40;
};
Demo of it in action
You can also use http://api.jquery.com/jquery.extend
as in:
var my = {
getName: function() {}
};
$.extend(my, {
getAge: function() {
}
});
Demo: http://jsfiddle.net/7KW3H/

KnockOutJS trigger parent function on child subscribe

I am currently trying to learn KnockOutJS. I thought it would be a great idea to create a simple task-list application.
I do not want to write a long text here, let's dive into my problem. I appreciate all kind of help - I am new to KnockOutJS tho!
The tasks are declared as followed:
var Task = function (data) {
var self = this;
self.name = ko.observable(data.name);
self.status = ko.observable(data.status);
self.priority = ko.observable(data.priority);
}
And the view model looks like this
var TaskListViewModel = function() {
var self = this;
self.currentTask = ko.observable();
self.currentTask(new Task({ name: "", status: false, priority: new Priority({ name: "", value: 0 }) }));
self.tasksArr = ko.observableArray();
self.tasks = ko.computed(function () {
return self.tasksArr.slice().sort(self.sortTasks);
}, self);
self.sortTasks = function (l, r) {
if (l.status() != r.status()) {
if (l.status()) return 1;
else return -1;
}
return (l.priority().value > r.priority().value) ? 1 : -1;
};
self.priorities = [
new Priority({ name: "Low", value: 3 }),
new Priority({ name: "Medium", value: 2 }),
new Priority({ name: "High", value: 1 })
];
// Adds a task to the list
// also saves updated task list to localstorage
self.addTask = function () {
self.tasksArr.push(new Task({ name: self.currentTask().name(), status: false, priority: self.currentTask().priority() }));
self.localStorageSave();
self.currentTask().name("");
};
// Removes a task to a list
// also saves updated task list to localstorage
self.removeTask = function (task) {
self.tasksArr.remove(task);
self.localStorageSave();
};
// Simple test function to check if event is fired.
self.testFunction = function (task) {
console.log("Test function called");
};
// Saves all tasks to localStorage
self.localStorageSave = function () {
localStorage.setItem("romaTasks", ko.toJSON(self.tasksArr));
};
// loads saved data from localstorage and parses them correctly.
self.localStorageLoad = function () {
var parsed = JSON.parse(localStorage.getItem("romaTasks"));
if (parsed != null) {
var tTask = null;
for (var i = 0; i < parsed.length; i++) {
tTask = new Task({
name: parsed[i].name,
status: parsed[i].status,
priority: new Priority({
name: parsed[i].priority.name,
value: parsed[i].priority.value
})
});
self.tasksArr.push(tTask);
}
}
};
self.localStorageLoad();
}
What I want to do in my html is pretty simple.
All tasks I have added are saved to localStorage. The save function is, as you can see, called each time an element has been added & removed. But I also want to save as soon as the status of each task has been changed, but it is not possible to use subscribe here, such as
self.status.subscribe(function() {});
because I cannot access self.tasksArr from the Task class.
Any idea? Is it possible to make the self.tasksArr public somehow?
Thanks in advance!
Try this:
self.addTask = function () {
var myTask = new Task({ name: self.currentTask().name(), status: false, priority: self.currentTask().priority() })
myTask.status.subscribe(function (newValue) {
self.localStorageSave();
});
self.tasksArr.push(myTask);
self.localStorageSave();
self.currentTask().name("");
};

Categories

Resources