Skip to content

Instantly share code, notes, and snippets.

@gintares
Created November 8, 2016 01:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gintares/c2542a29b119fb854e6f9aa614613fb3 to your computer and use it in GitHub Desktop.
Save gintares/c2542a29b119fb854e6f9aa614613fb3 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<!--
https://jsfiddle.net/b9baaw70/7/ - 2016-11-06
https://jsfiddle.net/b9baaw70/10/ - 2016-11-07
//Embed code
<script async src="//jsfiddle.net/b9baaw70/embed/js,html,result/"></script>
-->
<html>
<head>
<title>TODO supply a title</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
p {
text-align : justify;
}
</style>
</head>
<body>
<h2> Composite objects from the same bricks, but different functions </h2>
<p> Example of creation of composite objects from bricks, i.e. objects having only string/integer/array/object valued properties ( having no methods-functions),
and implementing `bind` or prototype to functionalise these composite object.
<br> It should be applied to objects, which have different functionalities although consist of the same bricks.</p>
<h2>
MAIN IDEA
</h2> <br> <p> Create brick-objects having only properties with string, number and array/object values.
<br> Such brick-objects can be easy copied, added and substracted using JSON.parse(JSON.stringify(obj)) fast, because they do not have methods.
<br> ( I do not show here how to add or substract two JSON objects in order to get a third object. )
<br> The HTML form example visualizes the idea:
<br>
<br> *) the bricks (for example HTML5 elements) compose the object (for example HTML5 form),
<br> *) `bind`, attaches different functionalities,
<br> *) simple helping function creates composite object copies.
<br> *) it is possible to use prototype instead of `bind`.
<br><br><br>See explanations in the code. Click or write in input to log the type of the event and id. </p>
<br>
<br> <div id="form1" > </div>
<br> <div id="form3" > </div>
<br> <div id="form4" > </div>
<h2>
CONCLUSION
</h2> <p> Create composite objects from objects having only properties,
and functionalise them either through prototype or using `bind`. </p>
<script>
/*
The MIT License (MIT)
Copyright (c) <2016-11-05> <copyright holder : Gintare Statkute, g.statkute@gmail.com > <version 1.2 >
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files, to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice with name of copyright holder and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
window.onload = function() {
/* example how to iterate
var obj1 ={ 'inputName' : {'name':'input', 'label':'(form1) Name:'} ,
'inputPasswd' : {'name':'input', 'label':'(form1) Password:'} };
for (item of Object.entries(obj1) ) { console.log(item) }; //obj1[Symbol.iterator] is not a function(…)
for (key of Object.keys(obj1) ) { console.log(key) }; //obj1[Symbol.iterator] is not a function(…)
*/
// HELPING FUNCTIONS
// Angus Croll //array, object, function
//https://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
function whatTypeFnc(obj) { return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() } ;
var copyComp = function ( form1 ){
var form2 = ( JSON.parse(JSON.stringify(form1)) );
for ( prop of Object.getOwnPropertyNames(form1) ) {
console.log(form1[prop]);
form2[prop] = form1[prop];
}
return form2;
}
// EXAMPLE
// ************************************************************
// Step1. brick object - HTML5 element
//could also be set, because every key is unique
var elemHTML5 = { elemtype:'elemHTML5' , name : 'elem4', label :'', value:'', id: '', bchecked:null, bhidden:null, title:'', style: { width : '10px'} } ;
// functions dealing with COmposite objects constructed from elemHTML
function setComp1( key, keyElem, value ) { this[key][keyElem] = value;};
//{ 'inputName' : {'name':'input', 'label':'(form1) Name:'} , 'inputPasswd' : {'name':'input', 'label':'(form1) Password:'} };
// function setCompM( obj ) { Object.assign(this, obj); } deletes not assigned values. I need to overwrite the give, and leave already existing
function setCompMany1( obj ) {
console.log(Object.keys(obj));
for ( prop of Object.keys(obj) ) {
if ( this[prop]) {
console.log( 'prop exists : '+prop);
for ( item of Object.keys(obj[prop]) ) {
console.log( ' seting item : '+ item)
if(!item instanceof Object ) { setCompMany1.call(this[prop], obj[prop][item] )}
else { this[prop][item] = obj[prop][item]; }
} } } };
function getComp(key) { return this[key]; };
function addComp(prop) { /* add property to elemHTML5 like, style.height, */ }
function removeComp(prop) { /* add property to elemHTML5 like, title, */ }
function initComp1() {
for ( property of Object.getOwnPropertyNames(this) ) {
// property == elemHTML5, property === elemHTML5, Right-hand side of 'instanceof' is not callable(…)
// console.log( " property = " + property );
if (this[property].elemtype) {
if(this[property].elemtype === 'elemHTML5' ) {
this[property].id = Math.floor(Math.random()*1000);
console.log( ' this['+property+'].id = ' + this[property].id );
} } } };
// function initComp2()
function displayComp1() {
var output="";
// console.log('************** displayComp 0 level'); console.log( Object.getOwnPropertyNames(this) );
for ( property of Object.getOwnPropertyNames(this) ) {
// set, get and display are strings, not functions
if( whatTypeFnc(property) !== 'function') {
// console.log('*** displayComp 1 level'); console.log(property);
// console.log( whatTypeFnc(property) );
// console.log( Object.keys(this[property] ) );
// set, get and display are strings, not functions, thus you have to check if they are arrays
if( Object.keys(this[property]).length > 0 ) {
output+= "<div>"+ this[property].label + ": <" + this[property].name +' id="'+ this[property].id+'"';
if(this[property].bchecked) { output+= this[property].bchecked; }
output+= ">" + this[property].value + "</" + this[property].name + "></div>" ;
} console.log(output);
} }
return output;
} ;
displayComp2 = function() { } //another way to display the form
EventHandler1 = function(event) {
console.log( 'tagName = '+event.target.tagName+', Id = '+event.target.id+', type='+event.type );
// a lot of code for different kind of events
}
EventHandler2 = function(event) {} // different way of handking various events
// here can be also methods enabling to set many keys, and keyElem4 values simultaneously,
// and other methods, dealing with validation , ajax, database
// also initialization method, dealing wuth default values, proerties and id creation at the moment of composite object creation
// ************************************************************
// Step2. Composite object - form constructor
var LoginFormFnc = function() {
this.inputName = ( JSON.parse(JSON.stringify(elemHTML5)) );
this.inputPasswd = ( JSON.parse(JSON.stringify(elemHTML5)) );
};
var form1 = new LoginFormFnc();
form1.init = initComp1.bind(form1);
form1.set = setComp1.bind(form1); // it is a string, not a function if you check using whatTypeFnc ( Object.getOwnPropertyNames(this) )
form1.setM = setCompMany1.bind(form1);
form1.display = displayComp1.bind( form1 ); //you can select the display method you want
form1.handleEvent = EventHandler1.bind( form1 ); // object can have one handleEvent method, but you can choose which functionality you want by assigning proper handler : handler1 or handler2, ...
// does not work : form1.prototype.set
// works
console.log( " *********************************** init form 1");
form1.init();
console.log(form1);
console.log( " *********************************** Setting form 1");
form1.setM( { 'inputName' : {'name':'input', 'label':'(form1) Name:'} ,
'inputPasswd' : {'name':'input', 'label':'(form1) Password:'} } );
/* form1.set( 'inputName','name','input');
form1.set( 'inputName','label','(form1) Name');
form1.set( 'inputName', 'id', Math.floor(Math.random()*1000) );
form1.set( 'inputPasswd','name','input');
form1.set( 'inputPasswd','label','(form1) Password');
form1.set( 'inputPasswd', 'id', Math.floor(Math.random()*1000) ); */
console.log(form1);
document.getElementById('form1').innerHTML = form1.display();
document.getElementById('form1').addEventListener("click", form1, false);
document.getElementById( 'form1' ).addEventListener("keyup", form1, false);
document.getElementById( 'form1' ).addEventListener("change", form1, false);
document.getElementById( 'form1' ).addEventListener("mousedown", form1, false);
document.getElementById( 'form1' ).addEventListener("mousedown", form1, false);
document.getElementById( 'form1' ).addEventListener("input", form1, false);
// ************************************************************
// Step3. copy the composite object
/* logs before c opying
console.log('form2'); console.log(form2);
console.log('Object.getOwnPropertyNames(form1) '); console.log( Object.getOwnPropertyNames(form1) );
console.log( Object.getOwnPropertyNames(form2) );
console.log(' Object.keys(form1)' );console.log( Object.keys(form1) );
console.log( Object.keys(form2) ); */
var form3 = copyComp(form1);
//add event listeners which you need - it could be part of bricks
document.getElementById('form3').innerHTML = form1.display();
document.getElementById('form3').addEventListener("click", form1, false);
document.getElementById( 'form3' ).addEventListener("keyup", form1, false);
document.getElementById( 'form3' ).addEventListener("change", form1, false);
document.getElementById( 'form3' ).addEventListener("mousedown", form1, false);
document.getElementById( 'form3' ).addEventListener("mousedown", form1, false);
document.getElementById( 'form3' ).addEventListener("input", form1, false);
/* logs after copying
console.log(' COPYING ');
console.log('Object.getOwnPropertyNames(form1) '); console.log( Object.getOwnPropertyNames(form1) );
console.log( Object.getOwnPropertyNames(form3) );
console.log(' Object.keys(form1)' );console.log( Object.keys(form1) );
console.log( Object.keys(form3) ); */
/* Uncommet if you want to set a new values for Titles .
// works - sets titles to form3
form3.set('inputName','name','input'); //TypeError: form2.set is not a function(…)
console.log(form1);
form3.set('inputName','label','(form 2) Name:');
console.log(form1);
form3.set('inputPasswd','name','input');
console.log(form1);
form3.set('inputPasswd','label','(form 2) Password:');
console.log(form3);
console.log(form3.inputName);
console.log(form3.inputName.name);
*/
document.getElementById('form3').innerHTML = form3.display();
// ************************************************************
// Step 4. Example that it is possible to achieve this using prototype (instead of bind). If one utilize prototype, no need for copying!
var LoginFormProtFnc = function() {
this.inputName = ( JSON.parse(JSON.stringify(elemHTML5)) );
this.inputPasswd = ( JSON.parse(JSON.stringify(elemHTML5)) );
};
LoginFormProtFnc.prototype.set = function( key, keyElem, value ){ this[key][keyElem] = value;};
//{ 'inputName' : {'name':'input', 'label':'(form1) Name:'} , 'inputPasswd' : {'name':'input', 'label':'(form1) Password:'} };
// function setCompM( obj ) { Object.assign(this, obj); } deletes not assigned values. I need to overwrite the give, and leave already existing
LoginFormProtFnc.prototype.setM = function ( obj ) {
console.log(Object.keys(obj));
for ( prop of Object.keys(obj) ) {
if ( this[prop]) {
// console.log( 'prop exists : '+prop);
for ( item of Object.keys(obj[prop]) ) {
// console.log( ' seting item : '+ item)
if(!item instanceof Object ) { setCompMany1.call(this[prop], obj[prop][item] )}
else { this[prop][item] = obj[prop][item]; }
} } } };
LoginFormProtFnc.prototype.init = function () {
for ( property of Object.getOwnPropertyNames(this) ) {
if (this[property].elemtype) {
if(this[property].elemtype === 'elemHTML5' ) {
this[property].id = Math.floor(Math.random()*1000);
// console.log( ' this['+property+'].id = ' + this[property].id );
} } } };
//LoginFormProtFnc.prototype.getLoginForm = function(key) { return this[key]; };
LoginFormProtFnc.prototype.display = function() {
var output="";
for ( property of Object.keys(this) ) {
// console.log(property);
// console.log( Object.keys(this[property] ) );
output+= "<div>"+ this[property].label + ": <" + this[property].name+' id="'+ this[property].id+'"';
if(this[property].bchecked) { output+= this[property].bchecked; }
output+= ">" + this[property].value + "</" + this[property].name + "></div>" ;
console.log(output);
}
return output;
} ;
LoginFormProtFnc.prototype.handleEvent= function(event) {
console.log(' tagName = '+event.target.tagName+', Id = '+event.target.id+', type='+event.type);
// normally here goes a lot of code
}
// here can be also methods enabling to set many keys, and keyElem4 values simultaneously,
// and other methods, dealing with validation , ajax, database
// also initialization method, dealing wuth default values, proerties and id creation at the moment of composite object creation
var form4 = new LoginFormProtFnc();
form4.init();
form4.setM( { 'inputName' : {'name':'input', 'label':'(form1) Name:'} , 'inputPasswd' : {'name':'input', 'label':'(form1) Password:'} } );
// works - sets titles to form4, no need to copy these methods separately from LoginFormProtFnc properties
// differently from `bind`, where you need to copy methods if you create a new Form.
form4.set('inputName','name','input');
form4.set('inputName','label','(form4) Name:');
form4.set('inputPasswd','name','input');
form4.set('inputPasswd','label','(form4) Password:');
console.log(form4.inputName);
console.log(form4.inputName.name);
document.getElementById( 'form4' ).innerHTML = form4.display(); // implemented only name and value attributes, left others as an excercise
document.getElementById( 'form4' ).addEventListener("click", form4, false);
document.getElementById( 'form4' ).addEventListener("keyup", form4, false);
document.getElementById( 'form4' ).addEventListener("change", form4, false);
document.getElementById( 'form4' ).addEventListener("mousedown", form4, false);
document.getElementById( 'form4' ).addEventListener("mousedown", form4, false);
document.getElementById( 'form4' ).addEventListener("input", form4, false);
// if you need to create a new form, no need for copying, just create form5 = new LoginFormProtFnc();
// if you make this with form3, you have to bind all the methods again, because they are not copied automatically
};
</script>
</body>
</html>
@gintares
Copy link
Author

gintares commented Nov 8, 2016

Example of creation of composite objects from simple one (bricks), and implementing bind or prototype to functionalise these composite object. Example of form Object from HTML5 elements, it`s initialisation, setting elements properties, and events.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment