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();
Related
In the following code example I want to somehow create a var myThingName via a functions arguments so I can avoid having to build global var names outside of the Module.
I have tinkered with window['myvarname'] = "yada"; But that's a global and feels like a complete hack.
In short, I am assuming I need to have a way to make many vars like : myThingName_1 myThingName_2 etc...
Background: I am trying to build a Canvas Element constructor to put any number of individual Canvas elements onto the screen. I am using various libraries like Paper.js Kinetic.js etc. When trying to streamline the production I built a Module wrapper but am now stuck at how to create unique var names for the libraries KLASS constructor for their various implementations of stage or scope.
Perhaps I am doing things totally incorrectly from the start.
But at the moment I am stumped as to how this goes about or what the name of the pattern I am looking.
var MagicThingModule = {
createThing : function(myThingNameAsString){
var myThingName = myThingNameAsString;
myThingName = new KLASS.Shape({ // code });
},
init : function(myThingNameAsString){
createThing(myThingNameAsString);
}
}
MagicThingModule.init("sendNameForThing_1");
How about:
var MagicThingModule = {
allMyThings: {},
createThing : function(myThingNameAsString){
var myThingName = myThingNameAsString;
myThingName = new KLASS.Shape({ // code });
this.allMyThings[myThingNameAsString] = myThingName;
},
// use me to et your thing back...
getMyThing: function(myThingName){
return this.allMyThings[myThingName];
},
init : function(myThingNameAsString){
createThing(myThingNameAsString);
}
}
and you can later reference it by:
var myThing = MagicThingModule.getMyThing("sendNameForThing_1");
Suppose I have created several widgets (mywidget1, mywidget2, ...) and that all have a method with the same name (doSomething).
To invoke the method I can use:
$("#elem").widget1("doSomething");
but this way I need to know the name of the widget (in the example widget1).
If I have an array with multiple instances of the various widgets, how can I invoke on each one the method "doSomething" without knowing the name of the widget?
You can't. Two options for you:
Store them in separate arrays (one array for widget1, another for widget2), or
Store objects in the arrays containing the information about which widget relates to that entry
Here's an example of #2
var list = [
{widget: "widget1", element: $("#elem")},
{widget: "widget2", element: $(".some-selector")},
{widget: "widget2", element: $("#another")},
{widget: "widget1", element: $("div .target")}
];
$.each(list, function(i, entry) {
entry.element[entry.widget]("doSomething");
});
In theory I suppose you could do something like the following:
var widgets = [widget1, widget2, widget3]; // etc, will assume they are defined already
// Access each Widget, now widget points to each of the widgets
// So we dont need to know the actual name like widget1, widget2 etc
widgets.forEach(function (widget) {
$('#elem').widget('doSomething'); // Call the doSomething method of this widget
$('#elem').widget.call(null, 'doSomething'); // Try this if the above fails
$('#elem').call(widget, 'doSomething'); // Or maybe this :)
}
Anyway try the above, of the top of my head im not sure which will work. I think what you are trying to do might be a bit difficult to implement, so sorry if it doesnt work. Hopefully it will :)
you can have an object of functions and get the key value dynamically to use the particular function
widgets = {
widget1 : function(value){return value;},
widget2 : function(value){return value+1},
widget3 : function(value){return value+2}
}; //you can have your list of functions say widgets1,2,3....
for(var k in widgets ){
console.log(widgets[k](1));
} //to get the function name you can use the key name in the object
Example on https://gist.github.com/vishnu667/44063d64f2d1210c26c9
you can also call the function if you know the key
widgets['widget2'](10); //to directly call the function if you know the key
to Dynamically add more widgets use
function add_widget(name,widgetFunction){ //function to add widget
widgets[name]=widgetFunction;
}
add_widget("widget4",function(value){return value+10;}); //adds a new widget to the widgets list
I found this solution:
before:
var tmp = $("#elem").widget1();
myarray[i] = tmp;
now:
var tmp = $("#elem").widget1();
myarray[i] = tmp.data(ui-widget1);
in this way it is possible to directly call the method:
myarray[x].doSomething();
what do you think?
can be an efficient solution?
thanks to all
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!
I've been coding JS for a while, but I've never did anything object oriented. I usually just defined all my variables at the top, and then just used them all. I kept hearing over and over to use OO, but now I can't do what I want and I can't get any help.
Here is a fiddle, along with semi identical code:
http://jsfiddle.net/zDeAJ/1/
var App = {
options: {
/* ------------------------------------
Options (PREFERABLY DONT CHANGE)
--------------------------------------- */
baseDomain : 'google.com',
apiVersion : '/api/v1'
},
state: {
current: App.options.baseDomain + App.options.apiVersion
}
}
So doing App.options.baseDomain (or this.options.baseDomain) won't work for me. What's the usefulness of defining Application level variables if I can't define other application level values based on them? I know this is a vague question but I really don't know what I'm asking... I just have a problem in that what I was easily able to accomplish with just a bunch of variables that held not only settings, but state within my application, is not so easy with my knowledge of Javascript OO patterns....
Edit: Alright, this is specifically what I want to do:
http://i.imgur.com/ak5YD.png
But I wasn't aware of the limitations... so I need a way around it, which sticks as close and elegant as possible to this implementation.
You can think of your approach as creating an "Instance" object called App.
Here's a slightly different approach.
function App () {
// Save a reference to the object
var that = this;
that.options = {
baseDomain: "google.com",
apiVersion: "/api/v1"
};
that.state = {
current: that.options.baseDomain + that.options.apiVersion
};
}
var myApp = new App();
// Write the current state to the screen
document.write(myApp.state.current);
Here's the JSFiddle: http://jsfiddle.net/zDeAJ/1/
Hope this helps!
Your question is quite generic; you should be more specific about what you're trying to accomplish.
That aside, read the documentation on objects;
Taken from w3schools:
With JavaScript you can define and create your own objects.
There are 2 different ways to create a new object:
1. Define and create a direct instance of an object.
2. Use a function to define an object, then create new object instances.
Way 1:
personObj=new Object();
personObj.firstname="John";
personObj.lastname="Doe";
personObj.age=50;
personObj.eyecolor="blue";
Way 2:
function person(firstname,lastname,age,eyecolor)
{
this.firstname=firstname;
this.lastname=lastname;
this.age=age;
this.eyecolor=eyecolor;
this.changeName=changeName;
function changeName(name) {
this.lastname=name;
}
}
Not one of you answered my question.... I thought about it a little... I could use a named function inside of the literal to access it... and if I wanted (not necessary) I could even assign it back to the options object
http://jsfiddle.net/zDeAJ/9/
var App = {
options: {
/* ------------------------------------
Options (PREFERABLY DONT CHANGE)
--------------------------------------- */
baseDomain : 'google.com',
apiVersion : '/api/v1',
blah: ''
},
state: function(){
this.options.blah = this.options.baseDomain + this.options.apiVersion;
}
}
App.state();
console.log(App.options.blah);
JavaScript is an interpreted language. That means your code is evaluated from the inside out or the most inner expression is evaluated and passed to the next outer expression.
In your example the value of options get's evaluated first and next the value of state. The problem is that you can't access the associative array of App before it is fully evaluated, wich is not the case during the evaluation of the value of state.
EDITED
Sorry for not answering correctly. Here is a refined approach from your second:
var App = {
options: {
/* ------------------------------------
Options (PREFERABLY DONT CHANGE)
--------------------------------------- */
baseDomain : 'google.com',
apiVersion : '/api/v1',
blah: ''
},
blah: function(){
return App.options.baseDomain + App.options.apiVersion;
}
}
console.log(App.blah());
You could do the following (in JavaScript, functions are objects):
function App () {
// Save a reference to the object
this.options = {
baseDomain: "google.com",
apiVersion: "/api/v1"
};
this.state = {
current: this.options.baseDomain + this.options.apiVersion
};
}
var myApp = new App();
console.log(myApp);
I'm sorry if this question has been asked before, but I'm not even sure what search terms to use to find the answer and when I try to search I never get anything specific to this question.
I'm using Javascript and I am wondering if it is possible to do something like this:
find(x); // find a document (for example)
find.inFolder(y); // find a folder's documents (for example)
In other words, can I have a function that can also be used as an object/class? I know I could run find() once and return a hash so that find.inFolder() would work, but I'm hoping there's a way where I could continue to call find().
Can it be done with prototype? (my "prototype" knowledge is very limited)
function find() {}
find.prototype.inFolder = function() {}
Can it be done inside a hash? [I know this code doesn't work]
var find = {
() : function() {},
inFolder : function() {}
}
To push it even further, is there a way to have the results of .inFolder() be sent to the find() function this way:
find().inFolder();
I know you might say that I don't understand the concept of javascript, and you'd be mostly correct, but I've seen people do some pretty amazing stuff with JS so I thought I'd ask the pros out there.
Thanks in advance for any help.
What you're describing is a Fluent interface (if you want something to search for). You could accomplish something like what you're trying to achieve like this:
var find = function() {
this.inFolder = function() {
return this; // Although to stop chaining, you could return nothing here.
};
return this;
};
find().inFolder(); // .inFolder().inFolder()...
This is a great pattern, especially when leveraged in projects like jQuery:
$("#element").find(".child_element").first();
Each call returns a jQuery object with .find(), .first() and many other functions, which lets you write intuitive and fluid code.
I kind of liked your find().inFolder() example, so here's an expanded version:
var find = function(file) {
this.folders = {
"Documents": ["Foo.txt", "Bar.txt"],
"Downloads": ["File.exe"],
"Misc": ["Picture.jpg"]
};
this.file = file;
this.inFolder = function(folder) {
var files = this.folders[folder];
return files.indexOf(this.file) >= 0;
};
return this;
};
alert(find("Foo.txt").inFolder("Documents")); // True
alert(find("File.exe").inFolder("Downloads")); // True
alert(find("Picture.jpg").inFolder("Downloads")); // False
http://jsfiddle.net/andrewwhitaker/TCdTd/
You can assign, a function to a member of another function:
find = function(x) { .... }
find.inFolder = function(y) { ... }
jsFiddle.
I'm not sure I understand the question however.