Preamble: I'm somewhat new to Javascript. I'm currently building a game with CraftyJS (if it matters). I'm making something akin to an RPG.
I want to model a bunch of types of messages which can be specified to show to the user. These can be:
A simple text message (reference)
A text message with an avatar image (reference)
A text message and a bunch of choices (reference)
Possibly more types in the future
Under a language like C# or Java, I would probably choose to model this as a Message class (perhaps with fields for avatar/choices), or with subclasses, or as an interface for my specific message types to implement.
In Javascript, classes are structured differently. What's the correct way to model this? I see a few options right now:
Anonymous objects with just a bunch of fields, eg. { text: "blah", choices: [ ... ] }
Some sort of class, eg. new Message(text, avatar, choices)
Insert option here
Since my game is data-driven, users will be creating JSON (possibly by hand) and specifying message fields. A slight advantage of a class, is that I force users to specify all necessary fields in the constructor (although I can still validate messages without classes if I just use anonymous objects).
What are my options and trade-offs (or what design should I use here?)
If the message "facility" is open ("Possibly more types in the future"), then you may want to investigate a different approach:
Have a class for the user interface that exposes the appropriate methods to manipulate it, e.g.:
function MessageUi() {
....
}
MessageUi.prototype.setText = function(text) { ... }
MessageUi.prototype.setPicture = function(imgUrl) { ... }
MessageUi.prototype.setChoices = function(choicesArray) { ... }
MessageUi.prototype.show = function(callback) { ... }
// and so on
The message classes are required to implement a single method, say render(ui). Example usage would be:
For the simple text case:
var msg = {
render: function(ui) {
ui.setText("My daddy used to hide...");
ui.show(); // no callback; clicking just goes on
}
};
For the choices case:
var msg = {
render: function(ui) {
ui.setText("Maria! So nice to meet you!");
ui.setChoices([
{value: 1, text: "Yes, sir. Very nice to meet you...."},
...
]);
ui.show(function(choice) { // callback gets selected choice
...
});
}
};
As new types of messages are needed, the existing ones need no modification. You will only add methods to MessageUi and the new message types will make use of them.
Just some thoughts, this is not intended to be a "final" solution (the question is quite open ended). Have fun developing the game!
Related
I am trying to pull in custom global settings I have created in the appropriate area that has an array field type.
I want to pull a global setting into the blog module to build out a field to add to the blog type. I tried using apostrophes model layer and the mongo db collections as described on the tutorial docs, but it would appear those need some req interaction or a self.method to fire correctly, which I don't think is available in the beforeConstruct function.
// This would ideally be a data.global.arrayField
var optionArraySet = ['Item 1', 'Item 2', 'Item 3'];
var formattedArraySet = [];
for (var type in optionArraySet) {
formattedArraySet.push({
label: optionArraySet[type],
value: optionArraySet[type]
})
}
options.addFields = [
{
label: 'Custom Array Field',
name: 'customArrayField',
type: 'select',
choices: formattedArraySet
}
].concat(options.addFields || [])
So optionArraySet would ideally by the global setting containing the data array I want to access.
For context, this global setting would apply to a couple different areas, and rather than have to update the code in the backend if we wanted to change the values, I could just add new values to the array set in the global settings and have the frontend pages update their display and settings fields update on the appropriate pieces.
If this helps, I am basically trying to control the tags that a content editor can pick on a particular piece type, so that they can't enter unwanted tags.
construct: function(self, options) {
self.beforeSave = function(req, piece, options, callback) {
var newTags = [];
newTags.push(piece.anotherFieldValue);
var customArrayField = piece.customArrayField;
for (var option in customArrayField) {
newTags.push(customArrayField[option]);
}
piece.tags = newTags;
return callback();
};
}
Thanks
Since global settings can be edited at any time, and beforeConstruct is only executed when the site starts up, it is not really useful to read the global settings there.
There are ways you could patch the schema at runtime, but there is a much simpler solution:
Create a new piece type module, "special-tags". Set name to special-tag.
Use a joinByArray schema field named _specialTags to join withType: 'special-tag'.
Now users can only pick from the list of special tags when editing a document that has this join in its schema, and admins can edit the special tags via the admin bar like any other piece type. The join loads with the document, so you can access, for instance, ._specialTags and find an array of docs with title properties containing the tag names.
For more information about joinByArray please see:
http://apostrophecms.org/docs/tutorials/getting-started/schema-guide.html#code-join-by-array-code
And the schema guide in general.
This is my first pass at this task i have. I need to update my UI based on the field. The field can be of different types. Here I am just checking for a memo or boolean type.
// UI Field Rule set.
var UIFieldRules = {
isMemo: function() {
return this.DataType === DataTypeKVP("Memo");
},
isBoolean: function() {
return this.DataType === DataTypeKVP("Boolean");
},
MapToList: function() {
if (UIFieldRules.isMemo.call(this) || UIFieldRules.isBoolean.call(this)) {
console.log("memo or bool");
console.log(UIFieldRules.isMemo.call(this));
console.log(this);
MAPTOLIST_SELECTOR.prop('disabled', true);
return;
} else {
MAPTOLIST_SELECTOR.prop('disabled', false);
console.log("UI field rules found memo");
}
}
};
I then call this object upon loading all the fields.
UIFieldRules.MapToList.call(field);
This works fine and satisfied the task, but now i need to apply more rules to the fields. (stop me if you heard this one before)
How can I get this set where i can just add a rule to a collection and have them all applied dynamically in javascript?
Update provide example:
function MapToList(field){
isBoolean:function(){}
isMemo : function(){}
execute : function(){
if (UIFieldRules.isMemo.call(this) || UIFieldRules.isBoolean.call(this)) {
console.log("memo or bool");
console.log(UIFieldRules.isMemo.call(this));
console.log(this);
MAPTOLIST_SELECTOR.prop('disabled', true);
return;
} else {
MAPTOLIST_SELECTOR.prop('disabled', false);
console.log("UI field rules found memo");
}
}
}
Then if i want to create more rules (which I do) should I create another object like the one above? Is there a best practice way of doing this in JS?
var rules = [];
rules.push(new MapToList(field));
rules.push(new RegExEnabled(field));
$.each(rules,function(item){
item.execute();
});
Your example approach is exactly fine. Create multiple objects that all implement the same interface, put them in a list, and then call a common method on each of them:
var rules = [MapToList, RegExEnabled];
rules.forEach(function(item){
item.execute(field);
});
However, you might want to notice that you typically you don't need a constructor + new if your object is not stateful or does not have any parameterisation, a simple object literal is enough.
And similarly, if your shared interface boils down to a single execute method, what you actually want is not a list of objects but just a list of functions you can call. It's not Java :-)
I have asp.net web application.where i am creating the User as registration for a particular Firm.In this firm there are 3 type of user as of now. as Admin, Dealer, Manager. so according to this I am changing the UI page.
Means When Admin (Admin is default entry) going create let say Dealer, then there is different UI except General information fields (like name ,contact details and all). and when creating the manager there is different UI fields, except than General information fields.
To reuse the page i am using this way , when selected Manager then related his UI fields get only visible, same for dealer. obviously there is a dropdown control from where i am selecting User type.
But some how , later on if one more User type get added by firm, I need to generate functionality according to New User type. how can i handle if I don not want to change existing code. sense is how can i write the generic code so that I should not need to change in code behind or in javascript again.
Where as all this things are in planning and under implementation for now. but before to go a head I must clear this thing. I am planning to change the UI structure in javascript as usual way , i means
if selected User Type is "Dealer then make visible these div's ELSE If User Type is Manager then make visible these divs
I want to write generic javacript , though new user type get added.
You could maintain the field <-> user type relation in a database and dynamically add labels and textboxes in the PageLoad of your Page.
Then when a new type of user comes along you'd simply need to add to your data.
Your user type should also be in a database.
Of course, the code that lets your users create other users would also have to be flexible enough. So you'd need a CanCreate table that dictates who can create what type of user. :)
EDIT Expanded how to build your Content
Actually, I like your idea about storing the html elements in the database. That'd save you using reflection to dynamically get the propertyvalue. However, I don't think you'll be able to use databinding and all that.
So instead I'd do something like this:
public enum ControlType
{
Label,
TextBox,
...
}
Make a table something like
UserType
PropertyName
PropertyLabelName
FieldLabelType (int)
FieldContentType (int)
Then on pageload you get the UserType, pull the data from the table, find the div where you want to put the data and then add the controls like:
(pseudocode)
Control label = null;
switch (FieldLabelType)
{
case ControlType.Label:
var label = new Label()
{
.. all kinds of properties
Text = PropertyLabelName
};
control = label;
break;
case ???
...
}
if (label != null)
fielddiv.Add(label);
Control field = null;
switch (FieldContentType)
{
case ControlType.TextBox:
var textbox = new TextBox()
{
.. all kinds of properties
Text = new Binding( ... Path = PropertyName)
};
control = textbox;
break;
case ???
}
if (field != null)
fielddiv.Add(field);
Of course, you'd need to do some positioning to get it all looking pretty. Perhaps chuck in a table or something in code?
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();
I am looking for a way to create an in-place editor in JavaScript like JEditable or similar plugins. However, the difference is that my editor would be a multi-field form rather than single field.
Since the same form will be used in different parts, i.e. editing existing items and creating new items, I would like to be able to specify the callback function for each case while not having to duplicate the shared functionality (e.g. validation, cancel button, etc.).
Currently I tried implementing it naively and the code looks awful as event handlers are scattering around the source code instead of being together as a component.
So my question is what is the recommended way to implement this? If there is a jQuery plugin that provide the functionality, that might be the easiest route. Otherwise, how would I structure my code properly?
Seperate the definition of your callbacks and do a lookup in the editable callback to your desired callback
var callbacks = { // define your callbacks here
'field1': function() {
alert("You editted field1");
},
'field2': function() {
alert("You editted field2");
}
}
$("input").each(function(i, o) {
$(o).editable('save.php', {
'callback': function(value, settings) {
// do validation etc.
// then call field-specific callback
if(callbacks[o.name]) { // I'm using 'name', but you could also use 'id' or something else
callbacks[o.name]();
}
}
});
});
The problem is that you need to separate the data from the displayed text. For each chunk of data you display, associate a json dict (written into the page, or pulled with ajax) with the text for each field in the form.
For example, if you're working with names (first, middle, last), you display the name like this:
<span class="editable">Colin M. Hansen</span>
and you write the form like this:
<form class="nameform" style="display: none;">
<input class="first" type="text">
<input class="mi" type="text">
<input class="last" type="text">
</form>
and you have a dict like this, mapped to the inputs in the form:
name1 = {
first: 'Colin',
mi: 'M',
last: 'Hansen'
};
The common code you write switches the form in for the text on the onclick of the span element, and fills each input field with data from the dict. The submit code saves the new data to name1 and to the server, and returns the new display text.