I started learning OOP in JavaScript at school and I have to do my homework in OOP. It is a shopping list, one fills in a product and price and that is put into an array, the array its contents are put in a table in HTML. The list also has got delete buttons for every product, I have thought about using this in JavaScript, as I am working in OOP, but I am sceptic because of the scope.
The idea is that one clicks "verwijder" on the webpage (EDIT: in the corresponding row) and the table row is gone. I have thought about using this but also about dynamically updating the table after removing the selected row from the array.
Who can advise me about what to do and how to do that correctly?
I have uploaded my JavaScript here: http://jsfiddle.net/hNAZ9/
If you want to study oop in javascript (and you are allowed to use frameworks), my suggestion is to learn a framework like http://knockoutjs.com/ . The separation between view/html interaction code and js object modeling code will make you life much easier. Here is an example reimplementation of your fiddle, using knockout: http://jsfiddle.net/kaGzz/3/
I think it's a lot clearer what the objects are doing:
var Item = function(product, price, parentArray){
var self = this;
self.product = ko.observable(product);
self.price = ko.observable(price);
self.deleteItem = function(){
parentArray.remove(self);
}
}
var ShoppingList = function(){
var self = this;
self.currentProduct = ko.observable();
self.currentPrice = ko.observable();
self.items = ko.observableArray();
self.addItem = function(){
self.items.push(new Item(self.currentProduct(), self.currentPrice(), self.items));
}
}
var shoppingList = new ShoppingList();
Related
I over complicated things at first and could not figure out how to create a list within a list using backbone.js. I finally got it, by simply creating a list item view for all of the players in my app. Then created a view for all of the teams inside my app.
I "glued" or "appended" them together by creating an app view that put them together, there is an each statement for both, before the two views were appended to the app view root, I appended the player list item view into the team view. Let me show you.
Here is my render method inside the app view: I am just not sure if this is a bad idea or not, I am thinking there are much better ways, but this is the only method I have had success with. It really makes sense to me, I can run events on each view without a problem
render: function() {
var self = this;
this.teams.each(function(team) {
var teamView = new TeamView({ model: team });
var teamHtml = teamView.render().el;
var teamPlayers = this.players.where({team_id: team.get('id')})
_.each(teamPlayers, function(player) {
var playerView = new PlayerView({ model: player });
var playerHtml = playerView.render().el;
$(teamHtml).append(playerHtml);
}, this);
this.$el.append(teamHtml);
}, this);
return this;
},
I asked about this and was told it would be better to create sub-views, well I am pretty sure this is a sub-view structure? Are there any holes to this method, if so I would like an explanation why this method is bad and how I can improve it. Last but not least I do care about clean maintainable code but what matters most is that I have teams wrap its respected players with an HTML result like below.
<div>
<ul class="lakers">
<li>Kobe</li>
<li>Pau</li>
</ul>
<ul class="spurs">
<li>Tony</li>
<li>Tim</li>
</ul>
</div>
Again id like some constructive criticism, mainly PROS & CONS with connecting the two views like that. Just needed to ask before I move on I want to make sure I am not getting into bad habits or creating problems in my code when I start expanding it, I am sure you understand that.
I asked about this and was told it would be better to create sub-views, well I am pretty sure this is a sub-view structure?
Yes you knew it, and TeamView is sub-view. However it's a "zombie view". Does it do anything itself? A view should be responsible of rendering itself, including appending its direct sub-views, but without knowing how to render its sub-views, i.e., you should pass the players collection to the TeamView and move the following logic into TeamView:
var teamPlayers = this.players.where({team_id: team.get('id')})
_.each(teamPlayers, function(player) {
var playerView = new PlayerView({ model: player });
var playerHtml = playerView.render().el;
$(teamHtml).append(playerHtml);
}, this);
I am building an SPA and everything is going well. It has multiple Viewmodels which are built dynamically and there can be multiple of the same kind, i.e you can open two calculators each having its own model which is bound to a specific div on the page.
Recently I realized that several of the viewmodels were requesting the same data from a web service and on a constant loop every 30 secs - 1 minute. So the same service call was being made multiple times every 30 seconds yet returning the same information.
So what I am trying to figure out is how I can create a "global" observableArray which multiple viewModels can be notified of a change and update rather than doing it themselves, this also helps to make sure the data on the page is consistent.
I was hoping I could do something like:
var GlobalData = (function() {
var commonData = ko.observableArray();
setInterval(function() {...go get data...commonData(data);}, 30000);
return {CommonData:commonData}
})();
ko.applyBindings(GlobalData, $('#RandomLonelyDiv')[0]);
Then later
function Calculator(element){
function init() { ko.applyBindings(calculator, $(element)[0]); }
var calculator = {
CommonData = GlobalData.CommonData
}
return calculator;
}
If it helps the only reason why I dont have a MainViewModel which contains all my other viewmodels is because i frankly dont know how to set that up for my environment.
I have a AppViewModel which contains a ko.observableArray called Windows, which is contains objects which define the options/information to build certain window types.
<!-- ko template:{name:'WindowTemplate', foreach:SelectedTab().Windows} --><!-- /ko -->
and then I have a custom Window binding that creates a modified kendoWindow, which creates a new viewmodel of a specific type such as Calculator, and like I said you could have multiple calculators at one time. But when I started this I wasnt really sure how to put that viewmodel into my AppViewModel. Perhaps its just another array?
It sounds like what you really need is a "Pub/Sub" model. That would allow you to publish and subscribe to messages that are ignorant of their generation or destination. Check out https://github.com/postaljs/postal.js/wiki.
I believe this may be what you are looking for: http://jsfiddle.net/xSKyR/474/
You can subscribe to another viewmodel's observable like so..
var ViewModel1 = function () {
var self = this;
self.something1 = ko.observable("1");
self.clickMe = function (data, event) {
self.something1("2");
};
};
var ViewModel2 = function () {
var self = this;
self.something2 = ko.observable();
vm1.something1.subscribe(function (newValue) {
self.something2(newValue);
});
};
var vm1 = new ViewModel1();
var vm2 = new ViewModel2();
ko.applyBindings(vm1, document.getElementById("vm1"));
ko.applyBindings(vm2, document.getElementById("vm2"));
I have an app that uses several different view models (this is the first time I have built an app with knockout js).
Basically what I'm doing is a wizard and each page is a knockout view model, at the end I'd like to take all the json from all the view models and submit it with a final button.
What would be the best way of doing this?
window.firstViewModel = new function()
{
var self = this;
self.firstProperty = ko.observable();
//
}
window.secondViewModel = new function()
{
var self = this;
self.secondProperty = ko.observable();
//
}
var submit = function()
{
var firstProperty = firstViewModel.firstProperty(); // access to firstViewModel
var secondProperty = secondViewModel.secondProperty(); // access to secondViewModel
//...
}
I would recommend going towards a Single Page Application.
Essentially, you would have a single view model with sections for each step in the wizard, and on submit, you have all the data you need.
The visibility/aesthetics can be controlled via css and intelligent binding.
RP Niemeyer has a good demo, and also talks about it in this answer.
Been getting into Knockout and and slowly getting used to it. Trying to use it in a new project, but am having a hard time getting things lined up to work. While I understand and can do simple examples (simple form with text boxes bound to ko.observables, or a table or list bound to a ko.observableArray), I can't get the syntax right for a combination, especially if I want to convert the data to JSON format in order to transmit it, via a webservice, to be saved into a database.
Basically it's a data entry form, with some text entry boxes, then a list of items (think company information + a list of it's employees).
I have a sample Fiddle here: http://jsfiddle.net/rhzu6/
In the saveData function, I just don't know what to do to get the data packaged. Doing ko.toJS(self) just shows "Object".
I tried defining the data as objects, but quickly got lost:
function Company(CompanyName, ZipCode) {
var self = this;
self.ZipCode = ko.observable(ZipCode);
self.CompanyName = ko.observable(CompanyName );
self.Employees = ko.observableArray();
}
function Employee(FirstName, LastNameB) {
var self = this;
self.FirstName = ko.observable(FirstName);
self.LastName = ko.observable(LastName);
}
Then the ViewModel looked like:
function viewModel() {
var self = this;
self.Company = ko.observable(); // company?
self.Employees = ko.observableArray(); // ?
}
But ran into the same issue. And also had binding problems - data-bind:"value: CompanyName" threw an exception saying it didn't know what CompanyName was...
Color me stumped. I'm sure it's something easy that I'm just missing.
Any and all help would be appreciated!
Thanks
You are looking for ko.toJSON which will first call ko.toJS on your ViewModel and afterwards JSON.stringify.
ko.toJS will convert your knockout model to a simple JavaScript object, hence replacing all observables etc. with their respective values.
I updated your Fiddle to demonstrate.
For more info, take a look at this post from Ryan Niemeyers blog.
An alternative is to make use of ko.utils.postJson:
ko.utils.postJson(location.href, {model: ko.toJS(viewModel) });
Notice the ko.toJS again.
It looks to me as if you (semantically) want to submit a form. Therefore, I think that you should use the submit binding. The biggest benefit is that you listen to the submit event, which allows submit by other means, such as Ctrl+Enter or any other keyboard combination you want.
Here is an example on how that submitEvent handler could look like. Note that it uses ko.mapper, which is a great way to create a viewModel from any JS/JSON-object you want. Typically, you would have
[backend model] -> serialization -> [JS/JSON-ojbect] -> ko.mapper.fromJSON(obj) -> knockout wired viewModel.
viewModel.submitEvent = function () {
if (viewModel.isValid()) { //if you are using knockout validate
$.ajax(
{
url: '/MyBackend/Add',
contentType: 'application/json',
type: 'POST',
data: ko.mapping.toJSON(viewModel.entityToValidateOnBackend),
success: function (result) {
ko.mapping.fromJSON(result, viewModel);
}
}
);
}
};
Good luck!
right now i am at a point where i feel that i need to improve my javascript skills because i already see that what i want to realize will get quite complex. I've iterrated over the same fragment of code now 4 times and i am still not sure if it's the best way.
The task:
A user of a webpage can add different forms to a webpage which i call modules. Each form provides different user inputs and needs to be handled differently. Forms/Modules of the same type can be added to the list of forms as the user likes.
My current solution:
To make the code more readable and seperate functions i use namespaced objects. The first object holds general tasks and refers to the individual forms via a map which holds several arrays where each contains the id of a form and the reference to the object which holds all the functions which need to be performed especially for that kind of form.
The structure looks more or less similar to this:
var module_handler = {
_map : [], /* Map {reference_to_obj, id} */
init: function(){
var module = example_module; /* Predefined for this example */
this.create(module);
},
create: function(module) {
//Store reference to obj id in map
this._map.push([module,id = this.createID()]);
module.create(id);
},
createID: function(id) {
//Recursive function to find an available id
},
remove: function(id) {
//Remove from map
var idx = this._map.indexOf(id);
if(idx!=-1) this._map.splice(idx, 1);
//Remove from DOM
$('#'+id+'').remove();
}
}
var example_module = {
create: function(id) {
//Insert html
$('#'+id+' > .module_edit_inner').replaceWith("<some html>");
}
}
Now comes my question ;-)
Is the idea with the map needed?
I mean: Isn't there something more elegant like:
var moduleXYZ = new example_module(id)
which copies the object and refers only to that form.... Something more logical and making speed improvements?? The main issue is that right now i need to traverse the DOM each time if i call for example "example_module.create() or later on any other function. With this structure i cant refer to the form like with something like "this"???
Do you see any improvements at this point??? This would help me very much!!! Really i am just scared to go the wrong way now looking at all the stuff i will put on top of this ;-)
Thank You!
I think you're looking for prototype:
=========
function exampleModule(id)
{
this.id = id;
}
exampleModule.prototype.create = function()
{
}
=========
var module1 = new exampleModule(123);
module1.create();
var module2 = new exampleModule(456);
module2.create();