Knockout cross class data binding - json

Goal: Give the user the ability to adjust mapping results.
I am running into an issue where I need to actually change the instance used of a data bound element. Disclaimer: My json data structure may be off and I am open to possible modifications. I am coding a table header mapping app so a user can verify that the headings mapped by the server are correct. The user will have the ability to map the headers if there is an issue. I have not been able to figure out how to actually update the data bound to the result when the select menu is changed. It feels like it should be super easy. I keep finding myself with basically a finished knockout app less the last step...
Markup Snippet:
<div id="wrapper" data-bind="foreach: headings">
<h1>Bind from this</h1>
<select data-bind="value: selectMenuIdVal, event: { change: updateListing }">
<option> </option>
<!-- ko foreach: $root.headings -->
<option data-bind="value: $data.CC_FIELD_ID, visible: $data.VENDOR_FIELD_NAME(), text: $data.VENDOR_FIELD_NAME"></option>
<!-- /ko -->
</select>
<h1>To this</h1>
<ul data-bind="foreach: listingFields">
<li data-bind="text: $data.VALUE"></li>
</ul>
</div>
KO Snippet:
var Heading = function(data) {
var self = this;
var heading = ko.mapping.fromJS(data, {}, this);
heading.selectMenuIdVal = ko.observable(heading.CC_FIELD_ID());
// heading.listingFields gets mapped by the mapping plugin
this.updateListing = function(ko_evt, js_evt) {
//TODO
// Get the listing results from the value of the select menu
// self.listingFields(those listings);
}
return heading;
}
Here is my fiddle: http://jsfiddle.net/breck421/SLT9B/1/

I really not sure if undertood you right.
Please let me know if this is what you are looking for :
this.updateListing = function (ko_evt, js_evt) {
console.log("fired");
//Do something freaking awesome!!!!!!!
if (vm.dataRepo) {
for (var i = 0; i < vm.dataRepo.HEADINGS.length; i++) {
if (vm.dataRepo.HEADINGS[i].CC_FIELD_ID == heading.selectMenuIdVal()) {
var listingFields = [];
for (var j = 0; j < vm.dataRepo.LISTINGS.length; j++) {
var listing = vm.dataRepo.LISTINGS[j];
var field = listing[i];
if (field) {
listingFields.push(field);
}
}
heading.listingFields(listingFields);
// heading.listingFields = listingFields;
}
}
}
}
See fiddle
I hope it helps.

Related

how to create html table results from JSON data

I have code that uses AJAX and JSON to output a chunk of SQL data when you do a search and I am trying to separate the data some and have it display into an HTML table. At first it was just the SQL data but I put some tags into the innerHTML line to at least visually separate it, however I would really like to be able to put each column into a separate table cell. Any ideas on how to do that would be greatly appreciated. Here is the code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="js/jquery-2.2.2.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<link href="css/bootstrap.min.css" rel="stylesheet">
<title>AJAX Search Example</title>
<script>
function fetch() {
// (A) GET SEARCH TERM
var data = new FormData();
data.append('search', document.getElementById("search").value);
data.append('ajax', 1);
// (B) AJAX SEARCH REQUEST
var xhr = new XMLHttpRequest();
// (CHANGE1) USING ONREADYSTATECHNAGE INSTEAD OF ONLOAD
xhr.onreadystatechange = function (event) {
// (CHANGE2) we will check if ajax process has completed or not it goes from 1,2,3,4 means end.
if(this.readyState == 4){
// (CHANGE2) when ready state comes to 4 we then check what response status was it if it is 200 good else error.
if(this.status == 200){
// (CHANGE3) MOVED ALL YOUR CODE HERE
// (CHANGE4) we need to use responseText instead of response because JSON comes as string that is why we are parsing it to be converted into array
var results = JSON.parse(this.responseText);
//I have added just a measure to check what the out put is you can remove it latter. open dev console to get the result.
console.log(results);
wrapper = document.getElementById("results");
if (results.length > 0) {
wrapper.innerHTML = "";
// (CHANGE5) UPDATED data ref with results
for (i = 0; i < results.length; i++) {
let line = document.createElement("div");
//it is just as simple to create id only it must start with alphabet not number
line.id=`res${[i]}`;
//we created span tag to display price and this is what we will change. on that span we will create a data-price attribute which will hold original price and we will run calculations using that number
//BIG CHANGE
//BIG CHANGE
//since after parsing individual record will be in Js object so we dont need to access them like array results[i]['item']
//we access them with dot notation results[i].item
line.innerHTML = `Category:${results[i].category} - OEM #:${results[i].oemnumber} - Price:$<span data-price='${results[i].price}'>${results[i].price}</span>
select discount >>
%70
%60
%50 100%`;
wrapper.appendChild(line);
}
// (CHANGE6) We moved event listeners here so any newly added elements will be updated.
//get all the links and apply event listener through loop
var links = document.querySelectorAll('a');
for ( ii = 0; ii < links.length; ii++) {
links[ii].addEventListener("click", function(event) {
//capture link value and get number to be converted to percentage
var percentage = event.target.innerText.match(/\d+/)[0]/100;
//capture the data-price which is within same div as anchor link
var pricetarget = event.target.parentElement.querySelector('[data-price]');
//get value of data-price
var actualprice= pricetarget.dataset.price;
//run math and chnage the value on display
pricetarget.innerHTML=(actualprice*percentage).toFixed(2);
});
}
} else { wrapper.innerHTML = "No results found"; }
} else {
//if reponse code is other ethan 200
alert('INTERNET DEAD OR AJAX FAILED ');
}
}
};
// (CHANGE7) We moved open event to end so everything is ready before it fires.
xhr.open('POST', "2-search.php");
xhr.send(data);
return false;
};
</script>
</head>
<body>
<!-- (A) SEARCH FORM -->
<form ID='myForm' onsubmit="return fetch();">
<h1>SEARCH FOR CATALYTIC CONVERTER</h1>
<input type="text" id="search" required/>
<input type="submit" value="Search"/>
</form>
<!-- (B) SEARCH RESULTS -->
<div id="results"></div>
</body>
</html>
Here is where I added the tags to at least visually separate it: "line.innerHTML = `Category:${results[i].category} - OEM #:${results[i].oemnumber} - Price:$${results[i].price}"
What I want to do is have Category, OEM #, and price each in a separate table cell. Thank you for any help offered.
You can simply generate trs inside your for (i = 0; i < results.len.. like you are already doing for divs . So , just use += to append every new tr inside tbody and then append this to your table
Demo Code :
//suppose json look like below :)
var results = [{
"category": "A",
"price": 13,
"oemnumber": "d1A"
}, {
"category": "B",
"price": 15,
"oemnumber": "d1B"
}, {
"category": "C",
"price": 12,
"oemnumber": "d1C"
}]
fetch();
function fetch() {
/* var data = new FormData();
data.append('search', document.getElementById("search").value);
data.append('ajax', 1);
var xhr = new XMLHttpRequest();
// (CHANGE1) USING ONREADYSTATECHNAGE INSTEAD OF ONLOAD
//some codes/..
console.log(results);*/
wrapper = document.getElementById("results");
wrapper.innerHTML = "";
var rows = "";
if (results.length > 0) {
for (i = 0; i < results.length; i++) {
//generate trs
rows += `<tr id=res${[i]}><td>${results[i].category}</td><td>${results[i].oemnumber}</td><td>$<span data-price='${results[i].price}'>${results[i].price}</span>
select discount >>
%70
%60
%50 100%</td></tr>`;
}
wrapper.innerHTML = `<table class="table">
<thead><th>Category</th><th>OEM</th><th>Price</th></thead><tbody>${rows}</tbody></table>`;
//sme other codes,,
}
};
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<div id="results">
</div>

Make basic search in array with if and else

I am trying to make a basic search function. if input.value does exist in array alert message, if not, push it to array ans show in HTML. I think I have already most of work done, but there is somewhere a mistake. Thank you in advance for your help guys .)
<div id="main">
<input id="inputForMyDict">
<button id="ButtonForInputSave" onclick="buttonSave()">Speichern</button>
<p id="demo"></p>
</div>
<script>
var myDict = [];
var buttonSave = function() {
for (var i = 0; i < myDict.length; i++) {
if (document.getElementById("inputForMyDict").value = myDict[i]) {
alert("your input is already in your list");
} else {
myDict.push(document.getElementById("inputForMyDict").value);
document.getElementById("demo").innerHTML = myDict;
}
}
}
In javascript, there are 2 ways to do a comparison.
Strict Equality Operator === strict equality operator.
If you are not sure about the exact datatype for the values being compared, then you can use the == for comparison.
The line document.getElementById("inputForMyDict").value = myDict[i] needs comparison operator and not the assignment operator (=). So you need to replace the = with either == or ===.
so your javascript code should look like
var buttonSave = function() {
for (var i = 0; i < myDict.length; i++) {
if (document.getElementById("inputForMyDict").value == myDict[i]) {
// If you know exact data type, then use the below line instead and comment the above line if (document.getElementById("inputForMyDict").value === myDict[i]) {
alert("your input is already in your list");
} else {
myDict.push(document.getElementById("inputForMyDict").value);
document.getElementById("demo").innerHTML = myDict;
}
}
}
Update1: Based on the clarification, provided by comments, you don't need to have a for loop to check for existence of element in array. Javascript provides a convenient way by indexOf method on an array. indexOf method check for the existence of an element in an array and returns the index of the element in the Array. However, if the element is not found then it returns -1.
Full code below which should work.
<!DOCTYPE html>
<html>
<body>
<div id="main">
<input id="inputForMyDict">
<button id="ButtonForInputSave" onclick="buttonSave()">Speichern</button>
<p id="demo"></p>
</div>
<script>
var myDict = [];
var buttonSave = function() {
//for (var i = 0; i < myDict.length; i++) {
var valueInTextbox = document.getElementById("inputForMyDict").value;
if(myDict.indexOf(valueInTextbox) > -1){
alert("your input is already in your list");
} else {
myDict.push(valueInTextbox);
document.getElementById("demo").innerHTML = myDict;
}
}
//}
</script>
</body>
</html>

Get the value of all checkbox when checkall checkbox is checked

I'am new to angularjs, I'm creating an application of attendance. When i check the checkall checkbox all the checkbox of name is also check and What i really wanted to achieve is to get the value of checked checkboxes. I'm done with checking all checkboxes. I just want to store all the value of checkboxes in an array. I can only get data when i check those checkboxes one by one. Thank you in advance.
In my html here is my code.
<ion-checkbox ng-model="Selected" ng-click="checkAll()">
<div class="wew">
Check All Checkbox
</div></ion-checkbox>
</label></div>
<table><tr><th><center>
List of Names
</center></th>
<th colspan="3">
Actions
</th></tr><tr><td><label>
<ion-checkbox ng-repeat="role in roles" ng-model="isChecked" ng-
change="format(isChecked,role,$index)"><div class="wew">
{{role}}
</div></ion-checkbox>
</label></td>
And in my controllers code. First this is my code where i get the list of names.
$http.post(link1, {section: section}).success(function(attendance){
for(a = 0; a<attendance.length; a++){
$scope.roles = [
attendance[0].Full_Name,
attendance[1].Full_Name,
attendance[2].Full_Name,
attendance[3].Full_Name,
attendance[4].Full_Name,
attendance[5].Full_Name,
attendance[6].Full_Name,
attendance[7].Full_Name,
attendance[8].Full_Name,
attendance[9].Full_Name,
]
}
})
.error(function(err) {
console.log(err)
})
And this is my code where i wanted to execute the checkall and automatically store the data in $scope.selected = []; if i click the check all checkbox..
$scope.checkAll = function () {
if ($scope.Selected) {
$scope.Selected = false;
} else {
$scope.Selected = true;
}
$scope.isChecked= $scope.Selected;
$scope.selected = [];
$scope.format = function (isChecked, role, $index) {
if (isChecked == true) {
$scope.selected.push(role);
}
else {
var _index = $scope.selected.indexOf(role);
$scope.selected.splice(_index, 1);
}
var students = $scope.selected;
console.log(students);
}
}
try this code
<script>
$(function(){
var numbers = $("input[type='checkbox']:checked").map(function(_, el) {
return $(el).val();
}).get();
});
</script>

adding a new row only when the last row in modified

Problem description:
I have a table with three rows. The first row contains a drop down. When a user selects a drop down option, a new row should be generated beneath the current last row. How can I tweak this code to such that a new row is generated only when the user selects a drop down option of the current last row, and not any other row?
JSFiddle: http://jsfiddle.net/JPVUk/13/
var ViewModel = function() {
var self = this;
self.items = ko.observableArray([{comment:'first comment', amount:0}]);
self.addNewItem = function(){
self.items.push(new Item('',0));
};
}
var Item = function(comment, amount) {
var self = this;
self.comment = ko.observable(comment);
self.amount = ko.observable(amount);
};
vm = new ViewModel()
ko.applyBindings(vm);
What I am struggling to do:
So, since I want to bind the change event to the last row, here's how I am approaching it:
<select class="input-small" data-bind="items()[items.length-1] ? event: { change: $root.addNewItem }">
This is however not working. Any ideas folks ?
Can't you just past the row that causes the event to fire to your handler and check it there?
Something like this:
<select class="input-small" data-bind="event: { change: $root.addNewItem }">
And then:
self.addNewItem = function(row){
if (row == self.items()[self.items().length - 1]) {
self.items.push(new Item('',0));
}
};
http://jsfiddle.net/JPVUk/14/
I'm not sure if jQuery was acceptable so this just uses DOM. Basically use the event object passed to knockout. Traverse a little dom and determine is the event target is a child of the last row in the parent table:
var tableRow = event.target.parentNode.parentNode,
body = tableRow.parentNode,
nodes = body.childNodes,
children = [];
for (var i = 0; i < nodes.length; i++) {
// remove non-element node types. ie textNodes, etc.
if (nodes[i].nodeType === 1) {
children.push(nodes[i]);
}
}
if (tableRow === children[children.length - 1]) {
self.items.push(new Item('', 0));
}

Angular.js using bootstrap and dynamically creating rows

I am trying to figure out how to dynamically create bootstrap row divs with a class of row-fluid with angular.js using the ng-repeat directive.
Here is the angular:
<div ng-repeat="task in tasks" class="row-fluid">
<div class="span6 well">{{task.name}}</div>
</div>
This does not work though. The bootstrap html I wish to generate is:
http://jsfiddle.net/YKkXA/2/
Basically I need to do mod 2 of the index inside of the ng-repeat, and if its 0, close out the </div> and create a new <div class="row-fluid">. How is this possible?
The idea is to filter your items in order to group them, and make a second ngRepeat to iterate on sub-items.
First, add that filter to your module :
module.filter('groupBy', function() {
return function(items, groupedBy) {
if (items) {
var finalItems = [],
thisGroup;
for (var i = 0; i < items.length; i++) {
if (!thisGroup) {
thisGroup = [];
}
thisGroup.push(items[i]);
if (((i+1) % groupedBy) === 0) {
finalItems.push(thisGroup);
thisGroup = null;
}
}
if (thisGroup) {
finalItems.push(thisGroup);
}
return finalItems;
}
};
});
In your controler (because if you filter directly in your template, then you will have a $digest loop):
function TaskCtrl() {
$scope.tasksGroupBy2 = $filter('groupBy')(taskGroup, 2);
}
And in your .html :
<div ng-repeat="taskGroup in tasksGroupBy2" class="row-fluid">
<div ng-repeat="task in taskGroup" class="span6 well">{{task.name}}</div>
</div>
As an improvement to the answer Anthony gave, I would say that you could save yourself a lot of trouble using the slice method instead of going through all those conditions.
Try defining your filter as it follows:
module.filter('group', function() {
return function(items, groupItems) {
if (items) {
var newArray = [];
for (var i = 0; i < items.length; i+=groupItems) {
if (i + groupItems > items.length) {
newArray.push(items.slice(i));
} else {
newArray.push(items.slice(i, i + groupItems));
}
}
return newArray;
}
};
});
After that you can call the filter on your controller as Anthony pointed out in his response:
function Controller ($scope) {
$scope.itemsGrouped = $filter('group')(itemsArray, 5);
}
Off Topic: using bootstrap you can just place divs of class="span6 well" into one bigass row as they will stack nicely. (You will get an responsive layout too). Sorry if it was just an example of the behaviour that bootstrap can't handle. Anthony and Remigio are right; you have to create an extra module vehicle that will generate divs immersed into your ng-repeated tags.