Arrays & JSON: Jquery $.each() only getting the last loops - javascript

Here's what I need to do.
I need to create an array of array of objects like the format below. I am getting name and values of input boxes and put then in a json then push it in an array and then I push the generated array into another array.
[
[
{"key1":"value1","key2":"value2"},
{"key1":"value3","key2":"value4"},
],
[
{"key1":"value1","key2":"value2"},
{"key1":"value1","key2":"value2"},
]
]`
below is my code
$scope.saveImages = function(){
var arrayOfArrays =[];
var arrayOfPhotos = [];
angular.element('.photo-group').each(function(){
var object = {};
$(this).find('.photo').each(function(){
var key = $(this).attr('name');
var value = $(this).val();
object[key] = value;
});
arrayOfPhotos.push(object)
arrayOfArrays.push(arrayOfPhotos)
console.log(arrayOfArrays)
});
}
and this is my markup
<div class="photo-group">
<div class="photo-group-body">
<div class="initial-photo">
<div class="row no-padding new-photo">
<div class="col no-padding">
<div class="form-group no-right-padding">
<label class="label-control" class="label-control">Label</label>
<input type="text" name="photo_label" class="form-control photo">
<input type="text" name="image_data" class="form-control photo photo_data">
</div>
</div>
</div>
</div>
<div class="initial-photo">
<div class="row no-padding new-photo">
<div class="col no-padding">
<div class="form-group no-right-padding">
<label class="label-control" class="label-control">Label</label>
<input type="text" name="photo_label" class="form-control photo">
<input type="text" name="image_data" class="form-control photo photo_data">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="photo-group">
<div class="photo-group-body">
<div class="initial-photo">
<div class="row no-padding new-photo">
<div class="col no-padding">
<div class="form-group no-right-padding">
<label class="label-control" class="label-control">Label</label>
<input type="text" name="photo_label" class="form-control photo">
<input type="text" name="image_data" class="form-control photo photo_data">
</div>
</div>
</div>
</div>
</div>
</div>
In my markup I have two .photo-group classes.
on the first .photo-group class I have four .photo class
and on the second .photo-group class I have two .photo class
so my array should look like this
[
[
{"photo_label":"value1", "image_data":"value2"},
{"photo_label":"value3", "image_data":"value4"},
],
[
{"photo_label":"value4", "image_data":"value5"},
]
]`
but instead, what I get is only the last objects in each array
[
[
{"photo_label":"value3", "image_data":"value4"},
],
[
{"photo_label":"value4", "image_data":"value5"},
]
]`

creat the pushing object inside foreach
angular.element('.photo-group').each(function(){
$(this).find('.photo').each(function(){
var object = {};
var key = $(this).attr('name');
var value = $(this).val();
object[key] = value;
arrayOfPhotos.push(object);
});
arrayOfArrays.push(arrayOfPhotos);
console.log(arrayOfArrays);
});

So after hours of trial and error testing and tracing the flow of the code.. I was able to get what I want.
Here's what I did
$scope.saveImages = function(){
var arrayOfArrays =[];
angular.element('.photo-group').each(function(){
var arrayOfPhotos = [];
$(this).find('.initial-photo').each(function(){
var object = {};
$(this).find('.photo').each(function(){
var key = $(this).attr('name');
var value = $(this).val();
object[key] = value;
});
arrayOfPhotos.push(object)
});
arrayOfArrays.push(arrayOfPhotos);
});
}
I added another loop to go deep into the child elements and place the var arrayOfPhotos = []; in the first instance of the loop

Related

KnockoutJS filtering an array

I have an observable array that contains a list of object that I want to filter through based on a user input. If the user searches a word that appears in the array in two different places then the filter function should return the title of both objects and delete or hide all other objects in the array that did not match the input from the user. I must use knockout js to preform this feature which is still new to me. Currently my filter function checks to see if the user input is included in a title of one of the objects within the array and if it is not then it removes the object. However, this not providing me what I need as it does not accurately filter the list.
My ViewMode
var viewModel = function() {
var self = this;
self.filter = ko.observable('');
self.locationList = ko.observableArray(model);
self.filterList = function(){
return ko.utils.arrayFilter(self.locationList(), function(location) {
if(location.title == self.filter()){
return location.title
}
else if( location.title.includes(self.filter()) ){
return location.title
}
else{
return self.locationList.remove(location)
}
});
};
}
The View
<section class="col-lg-2 sidenav">
<div class="row">
<div class="col-lg-12">
<div class="input-group">
<input data-bind="textInput: filter"
type="text" class="form-control" placeholder="Filter Places"
aria-describedby="basic-addon2" id="test">
<button data-bind="click: filterList id="basic-addon2">
<i class="glyphicon glyphicon-filter"></i>
Filter
</button>
</div>
</div>
<div class="col-lg-12">
<hr>
<div data-bind="foreach: locationList">
<p data-bind="text: $data.title"></p>
</div>
</div>
</div>
</section>
The answer to the question can be found here answered by Viraj Bhosale
ViewModel
var viewModel = function() {
var self = this;
self.filter = ko.observable('');
self.locationList = ko.observableArray(model);
self.filterList = ko.computed(function(){
return self.locationList().filter(
function(location){
return (self.filter().length == 0 || location.title.toLowerCase().includes(self.filter().toLowerCase()));
}
);
});
}
View
<main class="container-fluid">
<div class="row">
<section class="col-lg-2 sidenav">
<div class="row">
<div class="col-lg-12">
<div class="input-group">
<input data-bind="textInput: filter, valueUpdate: 'keyup'"
type="text" class="form-control" placeholder="Filter Places"
aria-describedby="basic-addon2" id="test">
</div>
</div>
<div class="col-lg-12">
<hr>
<div data-bind="foreach: filterList">
<p data-bind="text: $data.title"></p>
</div>
</div>
</div>
</section>
<section class="col-lg-10" id="map"></section>
</div>

Assigning a value to an object w/bracket notation

I am trying to write an app that will allow people to insert data into a form and this will draw a chart via Chart.js I am ok if there are not multiple datasets but when there are I look for a '/' and put values into an array. The problem I am having is I cant seem to get this form data. ie The chart I load initially has the two datasets properly setup. But I have tried using this code:
formData[index].value.forEach(function (value, dataset_index) {
console.log(formData[index].name);
chartData.datasets[dataset_index][formData[index].name] = value; // Problem Line
I get error "TypeError: chartData.datasets[dataset_index] is undefined". I have tried multiple permutations on this bracket notation and get similar errors.
// /public/main.js
const chartData = {
labels: ['M', 'T', 'W', 'T', 'F', 'S', 'S'],
datasets: [{
label: 'apples',
data: [12, 19, 3, 17, 6, 3, 7],
backgroundColor: "rgba(153,255,51,0.4)"
}, {
label: 'oranges',
data: [2, 29, 5, 5, 2, 3, 10],
backgroundColor: "rgba(255,153,0,0.4)"
}]
};
let Options = {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
};
const ctx = document.getElementById("myChart").getContext('2d');
let myChart = new Chart(ctx, {
type: 'bar',
data: chartData,
options: Options
});
// Get Chart Info from form
$(document).ready(function() {
$("#render_btn").on("click", function(e) {
e.preventDefault();
// First grab form data off the page
const formData = $('form').serializeArray();
// Get Chart Type and Options Seperate from Form Data
const chartTypeControl = document.getElementById("chart_type");
const chartType = chartTypeControl.options[chartTypeControl.selectedIndex].value;
Options = document.getElementById("chart_options").value;
// Create a data Object for Chart constructor to use
let datasetsItem = {};
// Convert formData array to chartData object
formData.forEach(function(value, index) {
if (formData[index].name == 'labels' || formData[index].name == 'options') {
chartData[(formData[index].name)] = formData[index].value;
} else {
// Check if this form value has multiple datasets(has a '/') and if so
// split the string into seperate dataset's
if (formData[index].value.indexOf('/') > -1) {
// Split the field up into seperate array items
formData[index].value = splitString(formData[index].value, '/');
// Now put the array items into their seperate datasets
formData[index].value.forEach(function (value, dataset_index) {
datasetsItem[formData[index].name] = value;
// console.log(datasetsItem[formData[index].name]);
chartData.datasets[dataset_index] = datasetsItem;
});
} else {
datasetsItem[formData[index].name] = formData[index].value;
chartData.datasets[0] = datasetsItem;
}
}
});
// =====================================================================================
// Now we have to do some converting i.e., chartData.labels must be converted to array
// from string etc.. ==================================================================
chartData.datasets[0].backgroundColor = splitString(chartData.datasets[0].backgroundColor);
chartData.datasets[0].borderColor = splitString(chartData.datasets[0].borderColor);
chartData.datasets[0].data = strToNumberArray(chartData.datasets[0].data);
chartData.labels = splitString(chartData.labels);
chartData.datasets[0].borderWidth = strToNumber(chartData.datasets[0].borderWidth);
if (isNaN(chartData.datasets[0].borderWidth)) {
alert("Attention: Border Width needs to be a number.");
}
// Check if successful
try {
if (!(chartData.datasets[0].data) || !(chartData.labels)) {
throw new Error("Input Error. Recheck your form data.");
}
myChart.type = chartType;
myChart.data = chartData;
myChart.update();
} catch (error) {
alert(error);
}
// ============================================================= //
// ============= Function definitions ========================== //
// ============================================================= //
function splitString(strToSplit, separator = ",") {
if (!strToSplit) {
alert("Error: One of your required fields is empty.");
return "";
}
// Test for a ',' or '/' slash in the string
const result = /[,\/]/g.test(strToSplit);
if (!result) {
// Only one entry in Data form
return strToSplit;
}
// Split a string into an array and trim any whitespace
let arrayOfStrings = strToSplit.split(separator);
arrayOfStrings.forEach(function(value, index) {
arrayOfStrings[index] = value.trim();
});
return arrayOfStrings;
}
// Function to convert string to an array then convert each element to a number
function strToNumberArray(str, separator = ',') {
if (str === undefined) {
alert('Error: string is empty.');
return "";
}
// Test for a comma in the string
const result = /,+/.test(str);
if (!result) {
alert(`Comma delimiter missing from ${str}`);
return false;
}
let arrayOfNumbers = str.split(separator).map(Number);
return arrayOfNumbers;
}
// Function convert string type to number
function strToNumber(str) {
Number(str);
return str;
}
// Functtion remove all whitespace from string
function removeWhiteSpace(str) {
str.replace(/\s+/g, '');
return str;
}
// ============================================================== //
// ================== End Function Definitions ================== //
// ============================================================== //
}); // End .on "click"
});
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.bundle.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<canvas id="myChart" width="400" height="400"></canvas>
<h2>Input your values:</h2>
<!-- Chart Input Form -->
<!-- We dont want chart_type as part of form when we use serializeArray
gather the data and use in chartData object -->
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="form-group">
<label for="chart_type">Chart Type</label>
<input type="text" name="type" class="form-control" id="chart_type" placeholder="Chart Type">
</div>
</div>
</div>
<form>
<div class="row">
<div class="col-md-4 col-md-offset-4 ">
<div class="form-group">
<label for="chart_type">Chart Type</label>
<select class="form-control input-lg" id="chart_type">
<option value="bar">Bar Chart</option>
<option value="pie">Pie Chart</option>
<option value="line">Line</option>
<option value="doughnut">Doughnut</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="form-group">
<label for="chart_Label">Chart Label</label>
<input type="text" name="label" class="form-control input-lg" id="chart_Label" placeholder="Chart Label">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-4">
<div class="form-group">
<label for="chart_labels">Chart Labels</label>
<input type="text" name="labels" class="form-control input-lg" id="chart_labels" placeholder="Apr, May, June Note:Seperated by Commas">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-4">
<div class="form-group">
<label for="chart_data">Chart Data</label>
<input type="text" name="data" class="form-control input-lg" id="chart_data" placeholder="i.e., 25 44 60 etc">
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 col-md-offset-4">
<div class="form-group">
<label for="chart_colors">Chart Colors</label>
<input type="text" name="backgroundColor" class="form-control input-lg" id="chart_colors" placeholder="i.e., red, blue, yellow, purple">
</div>
</div>
</div>
<!-- ============ More Options Markup ================================== -->
<button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#more_options" aria-expanded="false" aria-controls="collapseExample">
More Options
<span class="glyphicon glyphicon-chevron-down" aria-hidden="true"></span>
</button>
<div class="collapse" id="more_options">
<div class="well">
<div class="row">
<div class="col-md-6 col-md-offset-4">
<div class="form-group">
<label for="border_color">Border Color(s)</label>
<input type="text" name="borderColor" class="form-control input-lg" id="border_color" placeholder="i.e., red,blue,green,orange">
</div>
</div>
<div class="col-md-6 col-md-offset-4">
<div class="form-group">
<label for="border_width">Border Width</label>
<input type="text" name="borderWidth" class="form-control input-lg" id="border_width" placeholder="i.e., 1">
</div>
</div>
</div>
<!-- Options Markup -->
<div class="col-md-6 col-md-offset-4">
<div class="form-group">
<label for="chart_options">Chart Options</label>
<input type="text" name="options" class="form-control input-lg" id="chart_options" placeholder="See Chart.js Documentation">
</div>
</div>
</div>
</div>
</form>
<div class="row">
<div class="col-md-4 col-md-offset-5">
<button class="btn btn-primary btn-lg" id="render_btn">
<span class="glyphicon glyphicon-stats" aria-hidden="true"></span>
Render Graph
</button>
</div>
</div>
Long story short I cant seem to store the apples/oranges or any other values that are in multiple datasets into the chartData object array properly. Any help/insight much appreciated.
Update:
Yes I can now see the dataset array in chartData but it is incorrect. See image:
It would seem chartData.datasets{0] and 1 are exactly the same?? I updated the snippet. When I console.log datasets in the forEach I get the right values spitting out. Any suggestions ? Thanks.
From a glance, at your problem line, chartData.datasets[dataset_index] is not defined yet (as the error tells). You need to assign the element at that index. At the point you'll be trying to set the object key via a variable as seen here. So something like this should work:
var data = {};
data[formData[index].name] = value;
chartData.datasets[dataset_index] = data;

trying to sum a list of key value pairs in javascript

I am doing a dumb little coding challenge that has me stumped. I am trying to take a list of key value pairs like such:
John: 2
Jane: 3
John: 4
Jane: 5
Your objective is to sum the counts for each key in the textarea, and display the totals for each
key within the HTML document. The default value should result in the output, "The total for
John is 6. The total for Jane is 8."
I am trying to do this with no jquery or any other framework Here is what I have so far and I keep getting NaN for the value. I maybe have my logic reversed?
$(function() {
var keyStore = document.getElementById("keyValPairs");
if (!keyStore){
alert("you suck DIE!!!!!");
}
var hashTable = {};
var str = split(",");
for( var entry in str){
var a = entry.split(":")
if(!hashTable.hasOwnProperty(a[0])){
hashTable[a[0]] = 0;
}
hashTable[a[0]] += parseInt(a[1]);
}
console.log(obj);
});
I used the jquery function to wrap it because I don"t remember what the regular way of saying when the dom is loaded.
Html:
<div class="panel panel-primary">
<div class="panel panel-header">
<h1>Sum up your key value pairs</h1>
</div>
<div class="panel panel-body">
<div class="col-sm-6">
<textarea class="preescreen-input" name="keyValPairs" id="keyValPairs" cols="30" rows="10">
John : 2,
Jane: 3,
John : 4,
Jane : 5,
</textarea>
</div>
<div class="col-sm-2">
<button class="btn btn-primary">Sum</button>
</div>
<div class="col-sm-2">
<button class="btn btn-danger">
Insert
</button></br></br>
<label for="inputBoxKey">Enter Key</label>
<input type="text" id="inputBoxKey" name="inputBoxKey"></br></br>
<label for="inputBoxVal">Enter Value</label>
<input type=" text" id=inputBoxVal name="inputBoxVal">
</div>
<div class="col-2-offset-4 panel panel-footer">
<input type="text ">
</div>
</div>
</div>
Its just a stupid little programming challenge that I found online but none the less It has me perplexed. Any help would be appreciated.
You're calling split(",") without giving it a string to split on
when you do for( var entry in str), entry is the key, not the value of the entry instr
You need to bind the sum button to a function to run the code
You should trim your strings to remove whitespace
You should make sure names are valid and bail if not (to deal with blank lines)
Here's your code with these changes:
<div class="panel panel-primary">
<div class="panel panel-header">
<h1>Sum up your key value pairs</h1>
</div>
<div class="panel panel-body">
<div class="col-sm-6">
<textarea class="preescreen-input" name="keyValPairs" id="keyValPairs" cols="30" rows="10">
John : 2,
Jane: 3,
John : 4,
Jane : 5,
</textarea>
</div>
<div class="col-sm-2">
<button class="btn btn-primary" onclick="sum()">Sum</button>
</div>
<div class="col-sm-2">
<button class="btn btn-danger">
Insert
</button></br></br>
<label for="inputBoxKey">Enter Key</label>
<input type="text" id="inputBoxKey" name="inputBoxKey"></br></br>
<label for="inputBoxVal">Enter Value</label>
<input type=" text" id=inputBoxVal name="inputBoxVal">
</div>
<div class="col-2-offset-4 panel panel-footer">
<input type="text ">
</div>
</div>
</div>
and js:
window.sum = function() {
var keyStore = document.getElementById("keyValPairs");
if (!keyStore){
alert("you suck DIE!!!!!");
}
var hashTable = {};
var str = keyStore.value.split(",");
for( var key in str){
var entry = str[key].trim();
var a = entry.split(":");
if(a.length > 0) a[0] = a[0].trim();
if(a.length > 1) a[1] = a[1].trim();
if(a[0] == '') continue;
if(!hashTable.hasOwnProperty(a[0])){
hashTable[a[0]]=0;
}
hashTable[a[0]]+=parseInt(a[1]);
}
alert(JSON.stringify(hashTable));
}

Get info from dynamic/growing form

Have a form to create a contract, where that contract can have one or more users associated.
The area to input the users info, starts with only one field of one user, and one button to add more fields if needed.
<div id="utilizadores" class="row">
<div id="utilizador1" class="container-fluid">
<div class="row">
<div class="col-lg-5">
<input type="text" class="form-control" id="nomeUtilizador1" placeholder="Nome Utilizador">
</div>
<div class="col-lg-6">
<input type="text" class="form-control" id="funcaoUtilizador1" placeholder="Função">
</div>
</div>
</div>
</div>
This is the starting div
Clicking on Add User button it adds a new div under the "utilizador1"
<div id="utilizadores" class="row">
<div id="utilizador1" class="container-fluid">
<div class="row">
<div class="col-lg-5">
<input type="text" class="form-control" id="nomeUtilizador1" placeholder="Nome Utilizador">
</div>
<div class="col-lg-6">
<input type="text" class="form-control" id="funcaoUtilizador1" placeholder="Função">
</div>
</div>
</div>
<div id="utilizador2" class="container-fluid">
<div class="row">
<div class="col-lg-5">
<input type="text" class="form-control" id="nomeUtilizador2" placeholder="Nome Utilizador">
</div>
<div class="col-lg-6">
<input type="text" class="form-control" id="funcaoUtilizador2" placeholder="Função">
</div>
</div>
</div>
My question is, how can I get the number of users created, and insert them into a list using Javascript. The list will be a attribute of a Object (Contract).
What i have til now:
function test_saveItem() {
var contract = new Object();
contract.Dono = <% =uID %>;
contract.BoostMes = $("#boostMes").val();
contract.BoostAno = $("#boostAno").val();
var ListaUtilizadores = [];
var divs = document.getElementsByName("utilizador");
for (var i = 0; i < divs.length; i++){
var user = new Object();
user.Nome = $('#nomeUtilizador' + i).val();
ListaUtilizadores.push(user);
}
var test = JSON.stringify({ "contract": contract });
}
Any help appreciated
Edit: Got to a solution thanks to Shilly
List = [];
Array.prototype.slice.call(document.querySelectorAll('.user')).forEach(function (node, index) {
List.push({
"name" : document.getElementById('nameUser' + (index + 1)).value,
"job" : document.getElementById('jobUser' + (index + 1)).value
});
});
Something like this? But adding it into the addUser function as Super Hirnet says, will be more performant.
var divs = document.querySelector('#utilizadores').childNodes,
users = [];
Array.slice.call(divs).forEach(function (node, index) {
users.push({
"name" : divs[index].getElementById('nomeUtilizador' + (index + 1)).value
});
});
You can have an empty array and on every click of addUser put a new object into the array. The object can have information related to the added user.

How to add HTML contents Dynamically on Button Click Event with AngularJS

I need to add HTML content on Button Click event using AngularJS. Is it possible??
My index.html
<div class="form-group">
<label for="category"> How Many Questions Want You Add ? </label>
<div class="col-sm-10">
<input type="text" class="form-control input-mini" id="questionNos" name="questionNos" placeholder="Nos." ng-model="myData.questionNos">
<div class="input-append">
<button class="btn-warning btn-mini" type="button" ng-click="myData.doClick()">Generate</button>
</div>
</div>
</div>
I want to add Nos. of HTML divs as per quantity added dynamically..
myApp.js
angular.module("myApp", []).controller("AddQuestionsController",
function($scope) {
$scope.myData = {};
$scope.myData.questionNos = "";
$scope.myData.doClick = function() {
//Do Something...????
};
});
It should be possible. I would data-bind your Divs to viewModel elements, and in your doClick function create the viewModels.
I would avoid directly creating Html in your viewModel.
For example:
<div class="form-group">
<label for="category"> How Many Questions Want You Add ? </label>
<div class="col-sm-10">
<input type="text" class="form-control input-mini" id="questionNos" name="questionNos" placeholder="Nos." ng-model="myData.questionNos">
<div class="input-append">
<button class="btn-warning btn-mini" type="button" ng-click="myData.doClick()">Generate</button>
</div>
<div ng-repeat="q in myData.questions">
<!-- BIND TO Q HERE -->
</div>
</div>
</div>
And in doClick:
$scope.myData.doClick = function() {
var newQuestions = getNewQuestionViewModels($scope.myData.questionNos);
for (var i = 0; i < newQuestions.length; i++) {
$scope.myData.questions.push(newQuestions[i]);
}
};
You have to store questions in collection and do repeat.
DEMO
HTML:
<div>
<input type="text" ng-model="data.qcount">
<button type="button" ng-click="data.add()">Add</button>
</div>
<div>
<div ng-repeat="q in data.questions track by $index">
<pre>{{ q | json }}</pre>
</div>
</div>
JS:
$scope.data = {
questions: [],
qcount: 0,
add: function() {
var dummy = {
'title': 'Q title',
'body': 'Q body'
},
newQ = [];
for (var i = 0; i < $scope.data.qcount; ++i) {
newQ.push(dummy);
}
$scope.data.questions = $scope.data.questions.concat(newQ);
$scope.data.qcount = 0;
}
};

Categories