I need to increment key value when I'm saving data to localstorage because when I click button key is same for all records and record just update,doesn't creating new. I know I can do this with closures bit I'm fresh in JS and dont know how to do it correct.
(function() {
window.onload = function() {
document.getElementById('buttonCreate').onclick = function() {
var topicValue = document.getElementById("create-topic").value;
var statusValue = document.getElementById("create-status").value;
var descriptionValue = document.getElementById("create-description").value;
var key = 0;
var storage = new Storage();
var ticket = {
topic: topicValue,
status: statusValue,
description: descriptionValue
};
storage.set(key, ticket);
return (function() {
key++;
return key;
}());
}
}
})();
function Storage() {
this._ITEMS_DESCRIPTOR = 'items';
}
Storage.prototype.get = function() {
var fromStorage = localStorage.getItem(this._ITEMS_DESCRIPTOR);
return fromStorage ? JSON.parse(fromStorage) : [];
};
Storage.prototype.set = function(key, items) {
localStorage.setItem(key, JSON.stringify(items));
};
You need declare the variable before the function, to save the value and add 1+.
(function() {
window.onload = function() {
var key = 0; //move var key to here
document.getElementById('buttonCreate').onclick = function() {
var topicValue = document.getElementById("create-topic").value;
var statusValue = document.getElementById("create-status").value;
var descriptionValue = document.getElementById("create-description").value;
// var key = 0; remove these line
var storage = new Storage();
var ticket = {
topic: topicValue,
status: statusValue,
description: descriptionValue
};
storage.set(key, ticket);
return (function() {
key++;
return key;
}());
}
}
})();
When I used getFullName, getFirstName and getLastName work ok, but I can't use set functions setFullName, setLastName, setFirstName. My code:
var Person = function(firstAndLast) {
var fn=firstAndLast.split(' ');
var fstr=fn.join(' ');
var frn=fn[0];
var lsn=fn[1];
this.getFullName=function(){return fstr;};
this.getFirstName=function(){return frn;};
this.getLastName=function(){return lsn;};
this.setFirstName=function(a){fn[0]=a;};
this.setLastName=function(b){fn[1]=b;};
this.setFullName=function(c){fn=c.split(' ');};
};
What about this:
var Person = function(firstAndLast) {
var self = this;
this.fn = firstAndLast.split(' ');
this.frn = this.fn[0];
this.lsn = this.fn[1];
this.getFullName=function(){return self.fn.join(' ');};
this.getFirstName=function(){return self.frn;};
this.getLastName=function(){return self.lsn;};
this.setFirstName=function(a){self.frn=a; self.fn[0]=a;};
this.setLastName=function(b){self.lsn=b; self.fn[1]=b;};
this.setFullName=function(c){
self.fn = c.split(' ');
self.frn = this.fn[0];
self.lsn = this.fn[1];};
};
See this fiddle
If you have a lot of Person objects, you should consider moving the getter/setter functions to the class prototype:
var Person = function(firstAndLast) {
this.fn = firstAndLast.split(' ');
this.frn = this.fn[0];
this.lsn = this.fn[1];
};
Person.prototype.getFullName = function() {
return this.fn.join(' ');
}
Person.prototype.getFirstName = function() {
return this.lsn;
}
Person.prototype.getLastName = function() {
return this.lsn;
}
Person.prototype.setFirstName = function(a) {
this.frn=a;
this.fn[0]=a;
}
Person.prototype.setLastName = function(b) {
this.lsn=b;
this.fn[1]=b;
}
Person.prototype.setFullName = function(c) {
this.fn = c.split(' ');
this.frn = this.fn[0];
this.lsn = this.fn[1];
}
See updated fiddle
I have an object called status where I want to keep track of any status of a class.
Beside setting various statuses I also want to keep track of how long these have been active. Now instead of defining a second property for every status to track the time, this sounded like a job for getter / setter.
That's where I'm stuck. How do I make them dynamic so they trigger for each property of status?
var Person = function(options) {
this.name = options.name;
var _statusChanged = {};
var _status = {};
// How to make this dynamic?
var expr = "isOnfire";
this.status = {
get [expr]() {
console.log(_statusChanged);
return _status[expr];
},
set [expr](val) {
_status[expr] = val;
_statusChanged[expr] = new Date();
return _status[expr];
}
};
};
var John = new Person({
name: "John"
});
John.status.isOnfire = true;
John.status.hasPinkShirt = true;
console.log(John, John.status.isOnfire, John.status.hasPinkShirt);
If you have a list of these, just create the getters/setters in a loop, e.g.:
this.status = {};
["isOnFire", "hasPinkShirt"].forEach((name) => {
Object.defineProperty(status, name {
get() {
console.log(_statusChanged);
return _status[name];
},
set(val) {
_status[name] = val;
_statusChanged[name] = new Date();
return _status[name];
}
});
});
If they could be anything, then you'll want to use a Proxy object. With a proxy, you can capture all gets/sets without knowing property names in advance:
this.status = new Proxy(_status, {
get(target, propKey, receiver) {
// handle get
return _status[propKey];
},
set(target, propKey, value, receiver) {
// handle set
_status[propKey] = value;
_statusChanged[propKey] = new Date();
return true; // Tells the proxy the assignment worked
}
});
(Or you might use Reflect.get and Reflect.set, but even Firefox doesn't have them yet.)
Here's an article going into proxies in more detail.
Here's an example, but you'll need to run it in a recent version of Firefox because support or Proxy in the wild is still really thin on the ground, and by their nature, you can't shim/polyfill proxies.
(function() {
"use strict";
var _status = {};
var _statusChanged = {};
var status = new Proxy(_status, {
get(target, propKey, receiver) {
snippet.log(propKey + " requested");
return _status[propKey];
},
set(target, propKey, value, receiver) {
snippet.log(propKey + " set to " + value);
_status[propKey] = value;
_statusChanged[propKey] = new Date();
return true; // Tells the proxy the assignment worked
}
});
status.foo = "bar";
snippet.log("foo = " + status.foo);
})();
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Until you can use them, you'll need to make setting a status a method call, not an assignment.
You need an object called an ECMAScript 6 Proxy. In Firefox, they're on by default. At one point they were implemented in Chrome under "experimental JavaScript" but they seem to have been removed temporarily; see this ES6 compatibility table.
This code works in Firefox:
var output = function(text) {
var line = document.createElement('div');
line.innerHTML = text;
document.getElementById('output').appendChild(line);
}
var Person = function(options) {
this.name = options.name;
var _status = {};
var _statusChanged = {};
this.status = new Proxy(_status,{
get: function(target,property) {
return target[property];
},
set: function(target,property,value) {
_statusChanged[property] = new Date();
output("set " + property + " to " + value + " at " + _statusChanged[property]);
_status[property] = value;
}
});
this.show = function(property) {
output("Property " + property + " is " + _status[property] + " since " + _statusChanged[property]);
}
};
var John = new Person({
name: "John"
});
John.status.isOnfire = true;
John.status.hasPinkShirt = true;
John.show("isOnfire");
John.show("hasPinkShirt");
<div id="output"></div>
Maybe that would work for you
http://jsfiddle.net/oksbLyqf/16/
var Person = function (options) {
this.name = options.name;
var _statusChanged = {};
var _status = {};
var expr = '';
var addStatusProperty = function (prop) {
expr = prop;
Object.defineProperty(otherStatus, expr, {
get: function () {
console.log(_statusChanged);
return _status[expr];
},
set: function (val) {
_status[expr] = val;
_statusChanged[expr] = new Date();
return _status[expr];
}
});
};
var setStatusProperty = function (prop, val) {
expr = prop;
if (_status[expr]) {
otherStatus[expr] = val;
return _status[expr];
} else {
addStatusProperty(expr);
otherStatus[expr] = val;
return _status[expr];
}
};
var getStatusProperty = function (prop) {
expr = prop;
return _status[expr]
};
this.status = {
addProperty: addStatusProperty,
setProperty: setStatusProperty,
getProperty: getStatusProperty
};
var otherStatus = this.status;
};
var John = new Person({
name: "John"
});
John.status.setProperty('isOnfire', true);
John.status.setProperty('hasPinkShirt', true);
console.log(John, John.status.getProperty('isOnfire'), John.status.getProperty('hasPinkShirt'));
I fork the code from here:
http://kindohm.github.io/knockout-query-builder/
The code works nice on the client side.
But when I try to save the viewModel as JSON and then retrieve the data from the server the UI never refresh at all.
This is the original viewModel:
window.QueryBuilder = (function(exports, ko){
var Group = exports.Group;
function ViewModel() {
var self = this;
self.group = ko.observable(new Group());
// the text() function is just an example to show output
self.text = ko.computed(function(){
return self.group().text();
});
}
exports.ViewModel = ViewModel;
return exports;
})(window.QueryBuilder || {}, window.ko);
I be added the next method to the viewModel
self.Save = function () {
console.log(ko.toJSON(self));
}
Added this button to the view
<input type="submit" value="Save" data-bind="click: Save"/>
This is the Group viewModel:
window.QueryBuilder = (function(exports, ko){
var Condition = exports.Condition;
function Group(data){
var self = this;
self.templateName = data.templateName;
self.children = ko.observableArray(data.children);
self.logicalOperators = ko.observableArray(data.logicalOperators);
self.selectedLogicalOperator = ko.observable(data.selectedLogicalOperator);
// give the group a single default condition
self.children.push(new Condition());
self.addCondition = function(){
self.children.push(new Condition());
};
self.addGroup = function(){
self.children.push(new Group());
};
self.removeChild = function(child){
self.children.remove(child);
};
// the text() function is just an example to show output
self.text = ko.computed(function(){
var result = '(';
var op = '';
for (var i = 0; i < self.children().length; i++){
var child = self.children()[i];
console.log(child);
result += op + child.text();
op = ' ' + self.selectedLogicalOperator() + ' ';
}
return result += ')';
});
}
exports.Group = Group;
return exports;
})(window.QueryBuilder || {}, window.ko);
So when I press the "save" button the console show the JSON from this viewModel, everything fine here.
This is the JSON returned:
{"group":{"templateName":"group-template","children":[{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"},{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"},{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"}],"logicalOperators":["AND","OR"],"selectedLogicalOperator":"AND","text":"(Points = 0 AND Points = 0 AND Points = 0)"},"text":"(Points = 0 AND Points = 0 AND Points = 0)"}
I make a simple hack to avoid the connection to the server, so I take that json copy and paste on the load event and send to the constructor of the viewModel:
var vm;
window.addEventListener('load', function(){
var json = {"group":{"templateName":"group-template","children":[{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"},{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"},{"templateName":"condition-template","fields":["Points","Goals","Assists","Shots","Shot%","PPG","SHG","Penalty Mins"],"selectedField":"Points","comparisons":["=","<>","<","<=",">",">="],"selectedComparison":"=","value":0,"text":"Points = 0"}],"logicalOperators":["AND","OR"],"selectedLogicalOperator":"AND","text":"(Points = 0 AND Points = 0 AND Points = 0)"},"text":"(Points = 0 AND Points = 0 AND Points = 0)"};
vm = new QueryBuilder.ViewModel(json);
ko.applyBindings(vm);
}, true);
Then I modify the viewModel to recibe the json parameter
window.QueryBuilder = (function(exports, ko){
var Group = exports.Group;
function ViewModel(json) {
var self = this;
self.group = ko.observable(json.group);
// the text() function is just an example to show output
self.text = ko.computed(function(){
return self.group().text();
});
self.Save = function () {
console.log(ko.toJSON(self));
}
}
exports.ViewModel = ViewModel;
return exports;
})(window.QueryBuilder || {}, window.ko);
When I refresh the index.html the view is never loaded correctly and show this error on the JS console:
TypeError: self.group(...).text is not a function
return self.group().text();
Someone knows where is my mistake?
The last problem I had was related to the text() function on the child.
I fix this with the use of try/catch. So when the viewModel are new it have the text() function, but when this is loadad the text() does not exist, so I take the value directly from the "text" field.
try {
result += op + child.text();
}
catch(err) {
result += op + child.text;
}
The problem was on the Group class and Condition class.
This is the current and working code:
window.QueryBuilder = (function(exports, ko){
var Condition = exports.Condition;
function Group(data){
var self = this;
self.templateName = data.templateName;
self.children = ko.observableArray(data.children);
self.logicalOperators = ko.observableArray(data.logicalOperators);
self.selectedLogicalOperator = ko.observable(data.selectedLogicalOperator);
// give the group a single default condition
self.children.push(new Condition(data.children[0]));
self.addCondition = function(){
self.children.push(new Condition());
};
self.addGroup = function(){
self.children.push(new Group());
};
self.removeChild = function(child){
self.children.remove(child);
};
// the text() function is just an example to show output
self.text = ko.computed(function(){
var result = '(';
var op = '';
for (var i = 0; i < self.children().length; i++){
var child = self.children()[i];
try {
result += op + child.text();
}
catch(err) {
result += op + child.text;
}
op = ' ' + self.selectedLogicalOperator() + ' ';
}
return result += ')';
});
}
exports.Group = Group;
return exports;
})(window.QueryBuilder || {}, window.ko);
window.QueryBuilder = (function(exports, ko){
function Condition(data){
var self = this;
self.templateName = data.templateName;
self.fields = ko.observableArray(data.fields);
self.selectedField = ko.observable(data.selectedField);
self.comparisons = ko.observableArray(data.comparisons);
self.selectedComparison = ko.observable(data.selectedComparison);
self.value = ko.observable(data.value);
// the text() function is just an example to show output
self.text = ko.computed(function(){
return self.selectedField() +
' ' +
self.selectedComparison() +
' ' +
self.value();
});
}
exports.Condition = Condition;
return exports;
})(window.QueryBuilder || {}, window.ko);
Instead of self.group = ko.observable(json.group);, you should take a similar approach as you did on load self.group = ko.observable(new Group());, but this time pass the json.group data in Group
self.group = ko.observable(new Group(json.group));
I don't see where Group is defined, but you should make sure that it is able to handle and convert the JSON you now pass in, into observables.
Is there someone out there who can help me with this function. What it suppose to do is set a property in a string and this string is split firstly by a colon (:) for each control and the it checks if there is an id matching and if there is it then checks if there is a property matching if there is a property overwrite the value but my function doesn't seem to overwrite the property it just returns the original string. can someone help
var cookieValue = 'id=1&state=normal&theme=purple:id=2&state=maximized&theme=pink:id=3&state=maximized&theme=black';
var setProperties = function (cookie, id, prop, prop_value) {
var windows = cookie.split(':');
var result = $.each(windows, function(index, value) {
var temp1 = [];
if(value.indexOf(id) > -1) {
var temp2 = [];
var properties = value.split('&');
var result2 = $.each(properties, function(index, value) {
if(value.indexOf(prop) > -1) {
temp3 = [];
temp3 = value.split('=');
temp3[1] = prop_value;
temp2.push(temp3.join('='));
}else {
temp2.push(value);
}
return temp2.join('&')
});
temp1.push(result2.join('&'));
return temp1
}
else{
temp1.push(value);
}
return temp1;
})
return alert(result.join(':'));
}
setProperties(cookieValue, '2', 'theme', 'black');
Try:
function setProperties(cookie, id , name, value) {
var sections = $.map(cookie.split(":"), function (section) {
var pairs, found = false;
if (section.indexOf("id=" + id) === 0) {
pairs = $.map(section.split("&"), function (pair) {
if (pair.indexOf(name + "=") === 0) {
found = true;
return name + "=" + value;
} else {
return pair;
}
});
if (!found) {
pairs.push(name + "=" + value);
}
return pairs.join("&");
} else {
return section;
}
});
return sections.join(":");
}
Each doesn't return a value. You had some semicolons missing I edited the code a little.
It's not production worthy but at least it returns the (partially) correct value.
You will have to figure out how to replace that value in the cookie. I think regex is the best approach or of course you can pass temp1 array between the function but you will have to re-factor your code quite a lot.
var cookieValue = 'id=1&state=normal&theme=purple:id=2&state=maximized&theme=pink:id=3&state=maximized&theme=black';
var setProperties = function (cookie, id, prop, prop_value) {
var windows = cookie.split(':');
var result = $.each(windows, function(index, value) {
var temp1 = [];
console.log('value' + value);
console.log('windows' + windows);
console.log(cookieValue);
if(value.indexOf(id) > -1) {
var temp2 = [];
var properties = value.split('&');
var windows = $.each(properties, function(index, value) {
if(value.indexOf(prop) > -1) {
temp3 = [];
temp3 = value.split('=');
temp3[1] = prop_value;
temp2.push(temp3.join('='));
}else {
temp2.push(value);
}
cookieValue = temp2.join('&');
});
temp1.push(temp2.join('&'));
cookieValue = temp1;
}
else{
temp1.push(value);
}
cookeValue = temp1;
})
console.log("new cookie" + cookieValue); // PRINTS new cookieid=2&state=maximized&theme=black
}
setProperties(cookieValue, '2', 'theme', 'black');