How do handle empty arrays when serializing only altered form elements - javascript

I have a form that consists of a number of multi-select fields. Each select has a unique ID, and are named accordingly:
values[foobar1][]
values[foobar2][]
values[foobar3][]
... etc.
This form could potentially contain hundreds of these fields, and so is paged by ajax. The result of that is that there is no guarantee that all records are going to available at once at the front end. Therefore, it is impossible for me to submit the entire form. I do, however, have access to the entire list of records server-side.
My solution to this was to watch for changes in the form fields and, for every field that is changed, store the values in an array to keep track of just the altered field values. So if you make a change to just foobar2, the resulting serialized array that is sent to the server will look like this:
0: Object {
name: "values[foobar2][]"
value: "thevalue1"
},
1: Object {
name: "values[foobar2][]"
value: "thevalue3"
}
So this works fine except for, as you may have guessed, when the select multiple is emptied. No matter what format I use for storing the altered values, be it arraySerialization of each field or as an associative array, when I pass my array to $.param() for the ajax request the resulting serialized string contains no trace of the empty value. So there is no way for the server to determine that the value has been emptied.
Can anyone suggest a way of either passing the data to the server so that the empt(ied) array remains intact, or another way of dealing with the initial problem.
Thanks in advance!

You want to calculate the diff between current and previous state, send the change to the server, and apply it to the data.
You can do so using the JSON patch standard (rfc6902).
JSON Patch is a format for describing changes to a JSON document. It
can be used to avoid sending a whole document when only a part has
changed. When used in combination with the HTTP PATCH method it allows
partial updates for HTTP APIs in a standards compliant way.
To create the diff you can use an NPM module, such as jiff. A diff is set a of patching commands, that can transform a JSON document. For example:
[
{ "op": "replace", "path": "/values/foobar2/", "value": ["thevalue1"] },
{ "op": "remove", "path": "/values/foobar2/"}
]
You send the diff to the server, and then use a server module, such as php-jsonpatch, to apply the patch to the current data on the server.

Create a single object for all select field values you can use localStorage or sessionStorage to store it. Since the form is in a lot of pages and you use ajax to get each select field. Place the selected values of each field in an array. Creating an object like this is the idea.
{
formValues: {
foobar1: {
values: ["thevalue1","thevalue2"]
},
foobar2: {
values: ["thevalue3"]
},
...
foobarN: {
values: []
}
}
}
Every time you update a select vield value or values make sure to update the localStorage saved value. e.g.
var valuesObject = {
formValues: {
foobar1: {
values: ["thevalue1","thevalue2"]
},
foobar2: {
values: ["thevalue3"]
},
foobar3: {
values: []
}
}
}
// Put the object into storage
localStorage.setItem('valuesObject', JSON.stringify(valuesObject));
// Retrieve the object from storage
var valuesObjectA = localStorage.getItem('valuesObject');
//console.log('valuesObject: ', JSON.parse(valuesObjectA));
// post your data
$.post( "ajax.php", valuesObjectA ).done(function( data ) {
alert( "Data Loaded: " + data );
}).fail(function() {
console.log( "error" );
});
Sample fiddle

Related

couchdb views: return all fields in doc as map

I have a doc in couchDB:
{
"id":"avc",
"type":"Property",
"username":"user1",
"password":"password1",
"server":"localhost"
}
I want to write a view that returns a map of all these fields.
The map should look like this: [{"username","user1"},{"password","password1"},{"server","localhost"}]
Here's pseudocode of what I want -
HashMap<String,String> getProperties()
{
HashMap<String,String> propMap;
if (doc.type == 'Property')
{
//read all fields in doc one by one
//get value and add field/value to the map
}
return propMap;
}
I am not sure how to do the portion that I have commented above. Please help.
Note: right now, I want to add username, password and server fields and their values in the map. However, I might keep adding more later on. I want to make sure what I do is extensible.
I considered writing a separate view function for each field. Ex: emit("username",doc.username).
But this may not be the best way to do this. Also needs updates every time I add a new field.
First of all, you have to know:
In CouchDB, you'll index documents inside a view with a key-value pair. So if you index the property username and server, you'll have the following view:
[
{"key": "user1", "value": null},
{"key": "localhost", "value": null}
]
Whenever you edit a view, it invalidates the index so Couch has to rebuild the index. If you were to add new fields to that view, that's something you have to take into account.
If you want to query multiple fields in the same query, all those fields must be in the same view. If it's not a requirement, then you could easily build an index for every field you want.
If you want to index multiple fields in the same view, you could do something like this:
// We define a map function as a function which take a single parameter: The document to index.
(doc) => {
// We iterate over a list of fields to index
["username", "password", "server"].forEach((key, value) => {
// If the document has the field to index, we index it.
if (doc.hasOwnProperty(key)) {
// map(key,value) is the function you call to index your document.
// You don't need to pass a value as you'll be able to get the macthing document by using include_docs=true
map(doc[key], null);
}
});
};
Also, note that Apache Lucene allows to make full-text search and might fit better your needs.

How to save results from dynamic cascading drop down list?

I have 2 cascading drop down list , when page loads, data is loaded to the 1st DDL, when item is selected from this list its goes to the database and fetch matching items and populate 2nd DDL. I want to prevent going twice to the DB on selection.
For example, I am loading 1st DDL with cars manufactures then click on Toyota what happens next is it goes to DB and fetches all Toyota's models and populates the 2nd DDL, after that i select different car manufacture, same thing happens. Now when i select again Toyota from the 1st list it will not go to DB, it will pull the data from previous request.
I will like to keep an object (like dictionary) of the requests, so if item is already been requested it will not go back to the DB but use local saved data.
You can store list return from server through LocalStorage in Javascript. By using setItem()function as shown below.
window.localStorage.setItem('Toyota', 'Models');
Where Toyota is the key and Models is the value. Also note that LocalStorage can only store strings.
To store arrays or objects you would have to convert them to strings.
To do this we use the JSON.stringify() method before passing to setItem() .
const models = {
1: "Model-1",
2: "Model-2",
}
window.localStorage.setItem('Toyota', JSON.stringify(models));
Now when ever you select different car manufacture check its value in LocalStorage object first.
It accepts only one parameter which is the key and returns the value as a string.
To retrieve the Toyota key stored above:
window.localStorage.getItem('Toyota');
This returns a string with value as.
“{“1”:”Model-1”,”2”:”Model-2”}”
To use this value, you would have convert it back to an object.
To do this, we make use of JSON.parse() method which converts a JSON string into a Javascript Object.
JSON.parse(window.localStorage.getItem('Toyota'));
Please keep in mind to check weather your browser support Local storage or not.
if (typeof(Storage) !== "undefined") {
// Code for localStorage
} else {
// No web storage Support.
}
You can use Map() object to store the data based on key.
Find more information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
var brandModelsList = new Map();
function getBrandModels(brand)
{
if(brandModelsList.has(brand))
{
modelList = JSON.parse(brandModelsList.get(brand));
// Do stuff with second dropdown
}
//Your ajax stuff to get data
$.ajax({
url: 'server url',
data: {name: brand},
dataType: 'json',
}).done(response => {
brandModelsList.set(brand, JSON.stringify(response));
// Do stuff with second dropdown
});
}
It has support in most of modern browsers. A best tutorial on this is https://javascript.info/map-set-weakmap-weakset.
you can do this via JavaScript as #Rahat Hameed stated if the cascading dropdown values are not changed frequently, if they can be changed e.g (you can updated them via some logic).Then preferable ways it to fetch them from database.

Filter service in angularjs

I am rewriting an app that uses html, js and php into angular with rest php. I came across an issues regarding filters. In the actual form i have dropdowns that are populated ajax by js from php. The config in the js is made something like
form = new form('user-page');
form.ajaxselects = [ ['UserID', {table: 'users', DefaultValue: 5} ] ]
form.construct();
function form(formName) {
var this.ajaxselects;
function construct() {
foreach (this.ajaxselects as ajaxselect) {
$.post( ajaxselect[0], { Action:'get' },
function( data ) {
// exemplification, not real code
$(this).select(ajaxselect[0]).values = data;
}, 'json'
);
}
}
EDIT:
How it currently works with js:
I instantiate an object, called form and in that form i configure all my elements. Dropdowns, field validations etc.
After i configure (the configuration means that i say for a dropdown where to take the value from the server, what is the default value etc), for a validation i say what's the rule (it's mandatory, it's decimal).
And then when i run a function on the form object i go for each dropdown and populate it, add events validate the inputs etc.
I would like to know if i can and if yes, how, to build something like this in angular. To give to a service let's say a set of rules, something like:
['user_id', http_request:'server.com/api/populate/user_id', default_Value:1] and then i get the dropdown for users populated with the values. I don't know how to explain it better.

Backbone Model get attribute function returns last updated value

If there's an optional field in the server returned JSON top structure. Backbone Model seem to cache previously set value. Lets say i get a JSON like this
{
label: "test_label",
attr1: "test1",
attr2: "test2"
}
when I say #model.get("label") i get "test_label". So later on, if i get a JSON like this
{
attr1: "test1",
attr2: "test2"
}
i get "test_label" when i query for #model.get("label"). Is this a known issue in backbone.js? I do something like this to fetch
#modelXhr = #model.fetch
success: (-> this.trigger('reset')).bind #model
I'm a beginner in javascript/coffeescript, What can i do so when i query for a field which doesn't exist in the latest returned model I won't get an older value? Appreciate your help
You should either clear the model beforehand using #model.clear() or (in my opinion the better way) ensure that the data format for a specific model type does not change. Return { label: null, ... }
You are then later on able to check for the existence of the label using #model.get("label")?
If you are not able to ensure data integrity throughout your requests, clear the model.

$.post is changing array of objects to array of arrays

I try to post via ajax some data. Watch sreenshot1. But jQuery automatic converts object to array. This can be seen in screenshot2 (chrome developer tools).
Data send to $.post:
Data inspect in chrome developer tools:
Edited:
Problems appear in PHP where I expect for Data property to be array of objects:
I want to read data like this:
foreach ($_POST["Data"] as $field)
{
if ($field->Field == "username")
{
...
}
}
It's not really well spelled out in the PHP docs, but this is how it's supposed to work. For instance, the page on request variables has this to say about array-like cookies:
If you wish to assign multiple values to a single cookie variable, you may assign it as an array. For example:
<?php
setcookie("MyCookie[foo]", 'Testing 1', time()+3600);
setcookie("MyCookie[bar]", 'Testing 2', time()+3600);
?>
That will create two separate cookies although MyCookie will now be a single array in your script. If you want to set just one cookie with multiple values, consider using serialize() or explode() on the value first.
In other words, when you send in:
FormData[0][Field]: username
FormData[0][Value]: test3
PHP interprets this as an array of arrays, which looks like this:
array(1) {
[0]=> array(2) {
['Field'] => String(8) "username",
['Value'] => String(5) "test3"
}
}
And you can parse it like so:
foreach ($_POST["Data"] as $field)
{
if ($field['Field'] == "username")
{
...
}
}
Bear in mind that a PHP array is actually an ordered map. In other words, its perfectly suited for storing the key => value data you're passing in with your POST.
You seem to be expecting $_POST to contain simple objects (aka stdClass) and it simply doesn't. Most SOAP helpers in PHP will give you simple object responses (which is a typical blending of behaviors from PHP). See this question for some ideas if you really want to translate every $_POST["Data"] field into an object. But that would be expensive and unnecessary.
The answer is that you are in fact sending an array of objects - not an array of arrays as you've supposed. Here is an example of the data that you are sending to the server:
{
Operations: "Add",
TableName: "users",
Data: [
{
Field: "username",
Value: "test3"
},
{
Field: "someOtherField",
Value: "test4"
},
// ...
]
}
If on the PHP side you are expecting only an array of objects, and not an object containing an array of objects, then you should instead pass options.Parameters.Data, like:
var result = $.post(url, options.Parameters.Data, // ...

Categories

Resources