Using javascript prototype function to initialise variable in 'this' context - javascript-objects

I'm finding it difficult to explain in words, so here's a snippet of code I'm trying out but Firefox/firebug goes into tailspin!
I'm trying to follow this and this as a guide. What I'm trying to do here is
new MyObject.Method('string',optionsArray);
optionsArray items are iterated and saved using the prototype function Set()
if(typeof(MyObj) == 'undefined') MyObj= {};
MyObj.Method = function initialise(id,options)
{
this.id = id;
this.options = options;
this.properties ={};
for (var i = 0; i < this.options.length; i++) // =>options.length=2 (correct)
{
var obj = this.options[i];
//get the keynames, pass with values to Set() to update properties
for (var keys in obj)
{
console.log(keys); //=> correctly prints 'property1' and 'currentValue'
this.Set(keys,obj); //=> this is i guess where it enters a loop?
}
}
}
//sets properties
MyObj.Method.prototype.Set = function (name, value)
{
this.properties[name.toLowerCase()] = value;
}
and in my html page script block, i have
window.onload = function () {
var options = [
{ property1: {
show: true,
min: 0,
max: 100
}
},
{
currentValue: {
show: true,
colour: 'black'
}
}
];
var myObj = new MyObj.Method('someDivId',options);
}
please advise if I'm over complicating the code. I think checking for hasOwnProperty would help.

This should be a cleaner way of achieving what you want:
function MyObj(id, options) { // a function that will get used as the constructor
this.id = id;
this.options = options;
this.properties = {};
this.set(options); // call the set method from the prototype
}
MyObj.prototype.set = function(options) { // set the options here
for(var i = 0, l = options.length; i < l; i++) {
var obj = this.options[i];
for(var key in obj) {
if (obj.hasOwnProperty(key)) { // this will exclude stuff that's on the prototype chain!
this.properties[key] = obj[key];
}
}
}
return this; // return the object for chaining purposes
// so one can do FooObj.set([...]).set([...]);
};
var test = new MyObj('simeDivId', [...]); // create a new instance of MyObj
test.set('bla', [...]); // set some additional options
Note: For what hasOwnProperty is about please see here.

I made a declaration for MyObj and removed the function name initialise since you're obviously declaring this function to be a property of MyObj. Your final code will then be like below, and that runs for me just fine. Please note that you cannot actually call the function until after you declare the prototype function because else the object will have no notion of the Set function.
var MyObj = {};
MyObj.Method = function (id,options)
{
this.id = id;
this.properties ={};
for (var i = 0; i < options.length; i++) // =>options.length=2 (correct)
{
var obj = options[i];
//get the keynames, pass with values to Set() to update properties
for (var keys in obj)
{
console.log(keys); //=> correctly prints 'property1' and 'currentValue'
this.Set(keys,obj); //=> this is i guess where it enters a loop?
}
}
}
MyObj.Method.prototype.Set = function (name, value)
{
this.properties[name.toLowerCase()] = value;
}
var options = [
{ property1: {
show: true,
min: 0,
max: 100
}
},
{
currentValue: {
show: true,
colour: 'black'
}
}
];
var myObj = new MyObj.Method('someDivId',options);

var MyObj = {};
MyObj.Method = function initialise(id,options) {
this.id = id;
this.options = options;
this.properties = {};
for (var i = 0; i < this.options.length; i++)
{
var obj = this.options[i];
for (var keys in obj) {
this.Set(keys,obj[keys]);
//*fix obj => obj[keys]
// (and it should be singular key rather then keys
}
}
console.log(this.properties) // will output what you want
}
//sets properties
MyObj.Method.prototype.Set = function (name, value) {
this.properties[name.toLowerCase()] = value;
}
var options = [{
property1: {
show: true,
min: 0,
max: 100
}
},{
currentValue: {
show: true,
colour: 'black'
}
}];
var myObj = new MyObj.Method('someDivId',options);
this should work problem is you had your myObj = new MyObj... outside your onload event and options was out of its scope as it was declared as private variable to the anonymous function bound to the onload event.
I've fixed also the way you was copying the values to the properties as it doubled the names of the property and made it a bit messy.

Related

Isotope: Combined multiple checkbox and searchbox filtering

I'm trying to combine the Isotope multiple checkbox filtering with a searchbox.
I used the example with the checkbox filters from here and tried to implement the searchbox but with no luck.
Just the checkbox filtering works well. I think i'm close to the solution but my javascript skills are at a very beginner level.
I commented out the section of what i've tried to implement.
Thank you for some hints
// quick search regex
var qsRegex;
var $grid;
var filters = {};
var $grid = $('.grid');
//set initial options
$grid.isotope({
layoutMode: 'fitRows'
});
$(function() {
$grid = $('#grid');
$grid.isotope();
// do stuff when checkbox change
$('#options').on('change', function(jQEvent) {
var $checkbox = $(jQEvent.target);
manageCheckbox($checkbox);
var comboFilter = getComboFilter(filters);
/*var searchResult = qsRegex ? $(this).text().match(qsRegex) : true;
var filterResult = function() {
return comboFilter && searchResult;
}*/
$grid.isotope({
filter: comboFilter //or filterResult
});
});
});
function getComboFilter(filters) {
var i = 0;
var comboFilters = [];
var message = [];
for (var prop in filters) {
message.push(filters[prop].join(' '));
var filterGroup = filters[prop];
// skip to next filter group if it doesn't have any values
if (!filterGroup.length) {
continue;
}
if (i === 0) {
// copy to new array
comboFilters = filterGroup.slice(0);
} else {
var filterSelectors = [];
// copy to fresh array
var groupCombo = comboFilters.slice(0); // [ A, B ]
// merge filter Groups
for (var k = 0, len3 = filterGroup.length; k < len3; k++) {
for (var j = 0, len2 = groupCombo.length; j < len2; j++) {
filterSelectors.push(groupCombo[j] + filterGroup[k]); // [ 1, 2 ]
}
}
// apply filter selectors to combo filters for next group
comboFilters = filterSelectors;
}
i++;
}
var comboFilter = comboFilters.join(', ');
return comboFilter;
}
// use value of search field to filter
var $quicksearch = $('.quicksearch').keyup(debounce(function() {
qsRegex = new RegExp($quicksearch.val(), 'gi');
$grid.isotope();
}, ));
// debounce so filtering doesn't happen every millisecond
function debounce(fn, threshold) {
var timeout;
threshold = threshold || 100;
return function debounced() {
clearTimeout(timeout);
var args = arguments;
var _this = this;
function delayed() {
fn.apply(_this, args);
}
timeout = setTimeout(delayed, threshold);
}
}
function manageCheckbox($checkbox) {
var checkbox = $checkbox[0];
var group = $checkbox.parents('.option-set').attr('data-group');
// create array for filter group, if not there yet
var filterGroup = filters[group];
if (!filterGroup) {
filterGroup = filters[group] = [];
}
var isAll = $checkbox.hasClass('all');
// reset filter group if the all box was checked
if (isAll) {
delete filters[group];
if (!checkbox.checked) {
checkbox.checked = 'checked';
}
}
// index of
var index = $.inArray(checkbox.value, filterGroup);
if (checkbox.checked) {
var selector = isAll ? 'input' : 'input.all';
$checkbox.siblings(selector).prop('checked', false);
if (!isAll && index === -1) {
// add filter to group
filters[group].push(checkbox.value);
}
} else if (!isAll) {
// remove filter from group
filters[group].splice(index, 1);
// if unchecked the last box, check the all
if (!$checkbox.siblings('[checked]').length) {
$checkbox.parents('.option-set').find(selector).prop('checked', false);
}
}
I found the solution by myself, but i had to add a second function for returning the searchresult. Otherwise the search function is triggered only after using a checkbox or leaving the search box input field.
How could i avoid this redundand code?
JS:
// use value of search field to filter
var $quicksearch = $('.quicksearch').keyup(debounce(function() {
qsRegex = new RegExp($quicksearch.val(), 'gi');
$grid.isotope();
}, 200));
$(function() {
$grid = $('#grid');
$grid.isotope({
filter: function() {
var searchResult = qsRegex ? $(this).text().match(qsRegex) : true;
return searchResult;
}
});
// do stuff when checkbox change
$('#options').on('change', function(jQEvent) {
var $checkbox = $(jQEvent.target);
manageCheckbox($checkbox);
var comboFilter = getComboFilter(filters);
$grid.isotope({
filter: function() {
var buttonResult = comboFilter ? $(this).is(comboFilter) : true;
var searchResult = qsRegex ? $(this).text().match(qsRegex) : true;
return buttonResult && searchResult;
}
});
});
});

React state array object changes without setState

I have an array which is a state of the React component. This array is a checklist.
var units1 = this.state.units;
when I update units1, this.state.units changes without the this.setState({ units: units1 })
I use this.setState({ a: 2 }); just to see if the array was updated without this.setState({ units: units2 });
this.state.units gets its value from props so if the state changes the props also changes.
handleItemChange(e) {
var units1 = this.state.units.slice();
var unit_name = parseInt(e.target.attributes.getNamedItem('data-unit_name').value);
var new_unit;
if (!e.target.checked && this.state.units && this.state.units.length > 0) {
this.state.units.map((unit) => {
if (unit_name == unit.codpacunidad) {
if (unit.topics && unit.topics.length > 0) {
unit.topics.map((topic) => {
if (topic.codpacunidadtema == e.target.name) {
new_unit = unit;
var index = units1.indexOf(unit);
//units1.splice(index, 1);
units1 = update(units1, {$splice: [[index, 1]]})
var topics1 = unit.topics.slice();
index = topics1.indexOf(topic);
//topics1.splice(index, 1);
topics1 = update(topics1, {$splice: [[index, 1]]})
new_unit.topics = topics1;
}
});
}
}
});
} else {
var found_unit = false;
var name = parseInt(e.target.name);
var order = parseInt(e.target.attributes.getNamedItem('data-order').value);
var unit_order = parseInt(e.target.attributes.getNamedItem('data-unit_order').value);
if (this.state.units && this.state.units.length > 0) {
this.state.units.map((unit) => {
if (unit.codpacunidad == unit_name) {
found_unit = true;
new_unit = unit;
var index = units1.indexOf(unit);
units1.splice(index, 1);
var new_topic = {
codpacunidadtema: name,
orden: order
};
var topics2 = new_unit.topics;
new_unit.topics = update(topics2, { $push: [new_topic]});
}
});
}
if (found_unit == false) {
new_unit = {
codpacunidad: unit_name,
orden: unit_order,
topics: [{codpacunidadtema: name, orden: order }]
};
}
}
// var units2 = update(units1, { $push: [new_unit]});
// this.setState({ units: units2.sort(function(a, b) {
// return parseInt(a.orden) - parseInt(b.orden);
// })
// });
this.setState({ a: 2 }); //just to test if the array gets updated without this.setStaet({ units: units2 })
}
Anybody knows why this is happening?
As #Alexander van Oostenrijk said to make deep copy of array.
Because array are passed by reference which mean memory address of the array is passed not the value of array.
var units1 = this.state.units.slice();
Now units1 has the reference address of that array any change made to units1 or this.state.units.slice() will change value of both.As basically they are using address and if one change the value at address then both will read changed value.Hope you understand
To create deep copy you can create new object like
var units1 = Object.assign([],this.state.units)
This will create new object with data of this.state.units
extras I think you do not need .slice().

knockout push item to non-existing observableArray

From within a viewmodel I'm trying to dynamically push items to an observableArray. The ajax returns the data correctly.
html :
<li class="liTagulTagsChild" data-bind="click:$root.GetEmissions">/li>
javascript:
var TagDetail = function (di_titre, di_diffusion) {
this.di_titre = ko.observable(di_titre);
this.di_diffusion = ko.observable(di_diffusion);
}
var testA = [];
this.test = ko.observableArray(testA);
this.GetEmissions = function (c, event) {
var element = event.target;
var tag_id = element.getAttribute('tag_id');
$.AjaxAntiforgery({
url: 'Emission/GetDetailsByTagID/',
data: {
tag_id: tag_id
},
success: function (result) {
for (var i = 0; i < result.length; i++) {
var tD = new TagDetail(result[i].DI_TITRE, result[i].DI_DIFFUSION);
this.test.push(tD);
}
}
});
}
Problem: From within the GetEmissions function I cannot push items to this.test because this.test is null (not defined).
In other words,
this.test.push(tD);
fails. (null reference or not defined)
A solution anyone ?
In Javascript "this" doesn't mean what you think it means. Check out How to access the correct `this` context inside a callback?

checkbox filter for json array in Angularjs

I have create a filter but this filter is not working with array inside array.
'http://plnkr.co/edit/oygy79j3xyoGJmiPHm4g?p=info'
Above plkr link is working demo.
app.filter('checkboxFilter', function($parse) {
var cache = { //create an cache in the closure
result: [],
checkboxData: {}
};
function prepareGroups(checkboxData) {
var groupedSelections = {};
Object.keys(checkboxData).forEach(function(prop) {
//console.log(prop);
if (!checkboxData[prop]) {
return;
} //no need to create a function
var ar = prop.split('=');
//console.log("ar is - "+ar);
if (ar[1] === 'true') {
ar[1] = true;
} //catch booleans
if (ar[1] === 'false') {
ar[1] = false;
} //catch booleans
/* replacing 0 with true for show all offers */
if(ar[0]=='SplOfferAvailable.text'){
ar[1]='true';
}else{
}
//make sure the selection is there!
groupedSelections[ar[0]] = groupedSelections[ar[0]] || [];
//at the value to the group.
groupedSelections[ar[0]].push(ar[1]);
});
return groupedSelections;
}
function prepareChecks(checkboxData) {
var groupedSelections = prepareGroups(checkboxData);
var checks = [];
//console.log(groupedSelections);
Object.keys(groupedSelections).forEach(function(group) {
//console.log("groupedSelections- "+groupedSelections);
//console.log("group- "+group);
var needToInclude = function(item) {
//console.log("item- "+item);
// use the angular parser to get the data for the comparson out.
var itemValue = $parse(group)(item);
var valueArr = groupedSelections[group];
//console.log("valueArr- "+valueArr);
function checkValue(value) { //helper function
return value == itemValue;
}
//check if one of the values is included.
return valueArr.some(checkValue);
};
checks.push(needToInclude); //store the function for later use
});
return checks;
}
return function(input, checkboxData, purgeCache) {
if (!purgeCache) { //can I return a previous 'run'?
// is the request the same as before, and is there an result already?
if (angular.equals(checkboxData, cache.checkboxData) && cache.result.length) {
return cache.result; //Done!
}
}
cache.checkboxData = angular.copy(checkboxData);
var result = []; // this holds the results
//prepare the checking functions just once.
var checks = prepareChecks(checkboxData);
input.every(function(item) {
if (checks.every(function(check) {
return check(item);
})) {
result.push(item);
}
return result.length < 10000000; //max out at 100 results!
});
cache.result = result; //store in chache
return result;
};
});
above code is for check box filter.
when i click on checkbox called "Availability" it does not filter the result.
Please help me out.
Thanks.
I think that the way you are navigating through json is wrong because if you put in this way it works
"Location": "Riyadh",
"AvlStatus": "AVAILABLE"
"Rooms": {.....
You have to go in some way through Rooms and right now I think you're not doing that

Nodejs async data duplication

I'm having some problems with one async process on nodejs.
I'm getting some data from a remote JSON and adding it in my array, this JSON have some duplicated values, and I need check if it already exists on my array before add it to avoid data duplication.
My problem is when I start the loop between the JSON values, the loop call the next value before the latest one be process be finished, so, my array is filled with duplicated data instead of maintain only one item per type.
Look my current code:
BookRegistration.prototype.process_new_books_list = function(data, callback) {
var i = 0,
self = this;
_.each(data, function(book) {
i++;
console.log('\n\n ------------------------------------------------------------ \n\n');
console.log('BOOK: ' + book.volumeInfo.title);
self.process_author(book, function() { console.log('in author'); });
console.log('\n\n ------------------------------------------------------------');
if(i == data.length) callback();
})
}
BookRegistration.prototype.process_author = function(book, callback) {
if(book.volumeInfo.authors) {
var author = { name: book.volumeInfo.authors[0].toLowerCase() };
if(!this.in_array(this.authors, author)) {
this.authors.push(author);
callback();
}
}
}
BookRegistration.prototype.in_array = function(list, obj) {
for(i in list) { if(list[i] === obj) return true; }
return false;
}
The result is:
[{name: author1 }, {name: author2}, {name: author1}]
And I need:
[{name: author1 }, {name: author2}]
UPDATED:
The solution suggested by #Zub works fine with arrays, but not with sequelize and mysql database.
When I try to save my authors list on the database, the data is duplicated, because the system started to save another array element before finish to save the last one.
What is the correct pattern on this case?
My code using database is:
BookRegistration.prototype.process_author = function(book, callback) {
if(book.volumeInfo.authors) {
var author = { name: book.volumeInfo.authors[0].toLowerCase() };
var self = this;
models.Author.count({ where: { name: book.volumeInfo.authors[0].toLowerCase() }}).success(function(count) {
if(count < 1) {
models.Author.create(author).success(function(author) {
console.log('SALVANDO AUTHOR');
self.process_publisher({ book:book, author:author }, callback);
});
} else {
models.Author.find({where: { name: book.volumeInfo.authors[0].toLowerCase() }}).success(function(author) {
console.log('FIND AUTHOR');
self.process_publisher({ book:book, author:author }, callback);
});
}
});
// if(!this.in_array(this.authors, 'name', author)) {
// this.authors.push(author);
// console.log('AQUI NO AUTHOR');
// this.process_publisher(book, callback);
// }
}
}
How can I avoid data duplication in an async process?
This is because you are comparing different objects and result is always false.
Just for experiment type in the console:
var obj1 = {a:1};
var obj2 = {a:1};
obj1 == obj2; //false
When comparing objects (as well as arrays) it only results true when obj1 links to obj2:
var obj1 = {a:1};
var obj2 = obj1;
obj1 == obj2; //true
Since you create new author objects in each process_author call you always get false when comparing.
In your case the solution would be to compare name property for each book:
BookRegistration.prototype.in_array = function(list, obj) {
for(i in list) { if(list[i].name === obj.name) return true; }
return false;
}
EDIT (related to your comment question):
I would rewrite process_new_books_list method as follows:
BookRegistration.prototype.process_new_books_list = function(data, callback) {
var i = 0,
self = this;
(function nextBook() {
var book = data[i];
if (!book) {
callback();
return;
}
self.process_author(book, function() {
i++;
nextBook();
});
})();
}
In this case next process_author is being called not immediately (like with _.each), but after callback is executed, so you have consequence in your program.
Not sure is this works though.
Sorry for my English, I'm not a native English speaker

Resources