Setting select list value in Knockout.JS - javascript

I'm trying to populate a table of inputs (textboxes and select list) with JSON data retrieved from a jQuery GET request. For the example I pre-set a variable with some data rather than making a get request. The textbox inputs are correctly populating with data, but the select lists will not populate.
Here is an example of the data that knockout receives and places in the table
var returnedData = [{
"taskID": "1",
"taskName": "test task",
"taskDetails": "details",
"employee": {
"employeeID": "1",
"employeeName": "John"
}
}, {
"taskID": "2",
"taskName": "another test",
"taskDetails": "some more details",
"employee": {
"employeeID": "2",
"employeeName": "Jane"
}
}];
On the official knockout tutorials, they use a textarea (I included it in the fiddle) to show how the data is formatted as it is being posted back to the server. The pre-loaded data is in the exact same format.
Anyway, here is a fiddle with the code.

The reason that the select lists won't populate is object equality. They're bound to availableEmployees observable array with the options binding and the value binding is to Employee, but when you're setting the employee property of each task, you're setting it to a new object with the same properties and values, which is not equal in javascript. What I'd do is actually search (my example has a terrible for loop search, just to show you what I mean) for the matching employee in your list of available employees, and set the employee to that actual object, not the object coming in from the task's info. Check this out:
var returnedData = [{
"taskID": "2",
"taskName": "test task",
"taskDetails": "details",
"employee": self.availableEmployees()[1]
}, {
"taskID": "5",
"taskName": "another test",
"taskDetails": "some more details",
"employee": self.availableEmployees()[2]
}];
This is because in javascript:
var a = { foo: 1, bar: 'baz'};
var b = { foo: 1, bar: 'baz'};
console.log(a == b); // false
JSFiddle

Related

AngularJs appending options to select box

I am new to AngularJs. I am having problem in appending options to select boxes created by javascript. Following is my code.
var inputElements = $('<div><label style="float:left;">' + i + '</label><select ng-model="object" class="form-control sel" style="width:300px; float:right; margin-right:75px;"> <option>select</option></select></div></br></br>');
var temp = $compile(inputElements)($scope);
$('#projOrder').append(temp);
$scope.object = object;
//for(var opt=0; opt<selOptLabels.length; opt++) {
$('.sel').append('<option ng-repeat="obj in object" value="'+
{{obj.value}}+'">'+{{obj.value}}+'</option>');
I am getting this error:- SyntaxError: invalid property id
Hi, I am posting json example. This is just a small part of json in my case.
"ProjectOrder": {
"Connect direct required": {
"value": "N",
"id": "STR_15523_62"
},
"Cores": {
"value": ".5",
"id": "NUM_15523_50"
},
"Permanent data in GB": {
"value": "100",
"id": "NUM_15523_56"
},
"Description": {
"value": "AZBNL azbngb",
"id": "STR_15523_2"
},
"Order Id": {
"value": "15523",
"id": "INT_15523_96"
},
"Project cost center": {
"value": "N",
"id": "STR_15523_66"
},
"Project debitor": {
"value": "N",
"id": "STR_15523_64"
},
"Project OE": {
"value": "N",
"id": "STR_15523_57"
},
"Project SITE": {
"value": "N",
"id": "STR_15523_59"
},
"Project Status": {
"value": "RFC",
"id": "STR_15523_54",
"dropdown": [
{
"value": "IW",
"label": "In Work"
},
{
"value": "RFC",
"label": "Ready for Creation"
},
{
"value": "CR",
"label": "Created"
},
{
"value": "FC",
"label": "Failed"
}
]
},
"Project Type (paas, miner)": {
"value": "paas",
"id": "STR_15523_37",
"dropdown": [
{
"value": "paas",
"label": "PaaS Project"
},
{
"value": "miner",
"label": "Miner Project"
}
]
},
"WORK data in GB": {
"value": "100",
"id": "NUM_15523_55"
}
}
Now I have to create input fields and dropdown menus(if there is a dropdown menu) with json data
You really should not be hand-constructing HTML like that. It's best if you use a template and let the template engine handle the heavy lifting.
I also noticed that you're using object as the ng-model. Instead you should have a separate variable which will hold the selected value.
Here's a better way of doing this--in an .html file:
<div ng-repeat="object in listOfObjects"
<label style="float: left">{{ $index }}</label>
<select ng-model="selectedValues[$index]" class="form-control sel"
style="width:300px; float:right; margin-right:75px;"
ng-options="obj.value for obj in object"></select>
</div>
Then in whatever controller you have set up in JavaScript:
// this will be the list of selected values
$scope.selectedValues = new Array(list.length);
// this would be the array that each `object` is part of
$scope.listOfObjects = list;
This isn't the most elegant solution, but basically what I've done is construct an array that is the same length as the list of objects. Angular templates have a special variable $index when you're in an ng-repeat which tracks the current index of the array you're looping through.
So when a user changes the selected value of the 3rd select box (index 2), $scope.selectedValues[2] would be set to the selected option.
EDIT: on transforming the JSON to an array:
var list = Object.keys(json).map(function(jsonKey) {
return {
name: jsonKey,
label: json[jsonKey].label,
value: json[jsonKey].value
};
});`
So.. there are a number of reasons why that won't work. The provided code wouldn't even work because of the template brackets that you are trying to append to your html string...
$('.sel').append('<option ng-repeat="obj in object" value="' +{{obj.value}}+'">'+{{obj.value}}+'</option>');
Is there a reason that you are trying build your markup in js?
It's also advised not to use jquery inside angular controllers. If you have jquery loaded the jQuery object is available through angular.element, otherwise angular uses jQuery light.
Rather than enumerate on the other issues here, I put together this basic example of how a select works in Angular
https://codepen.io/parallaxisjones/pen/BRKebV
Also, you should consult the angular documentation before posting questions to stack overflow. The docs provide a pretty clear example of how to use ng-repeat in a select. https://docs.angularjs.org/api/ng/directive/select
EDIT: I updated my codepen with an example of fetching JSON data with an HTTP GET request
EDIT: updated codepen with provided data example, iterating over object with (key, value) in json syntax in ng-repeat

How to get specific array from JSON object with Javascript?

I am working with facebook JS SDK which returns user's information in JSON format. I know how to get the response like response.email which returns email address. But how to get an element from a nested array object? Example: user's education history may contain multiple arrays and each array will have an element such as "name" of "school". I want to get the element from the last array of an object.
This is a sample JSON I got:-
"education": [
{
"school": {
"id": "162285817180560",
"name": "Jhenaidah** School"
},
"type": "H**hool",
"year": {
"id": "14404**5610606",
"name": "2011"
},
"id": "855**14449421"
},
{
"concentration": [
{
"id": "15158**968",
"name": "Sof**ering"
},
{
"id": "20179020**7859",
"name": "Dig**ty"
}
],
"school": {
"id": "10827**27428",
"name": "Univer**g"
},
"type": "College",
"id": "9885**826013"
},
{
"concentration": [
{
"id": "108196**810",
"name": "Science"
}
],
"school": {
"id": "2772**996993",
"name": "some COLLEGE NAME I WANT TO GET"
},
"type": "College",
"year": {
"id": "1388*****",
"name": "2013"
},
"id": "8811215**16"
}]
Let's say I want to get "name": "some COLLEGE NAME I WANT TO GET" from the last array. How to do that with Javascript? I hope I could explain my problem. Thank you
Here is a JsFiddle Example
var json = '{}' // your data;
// convert to javascript object:
var obj = JSON.parse(json);
// get last item in array:
var last = obj.education[obj.education.length - 1].school.name;
// result: some COLLEGE NAME I WANT TO GET
If your json above was saved to an object called json, you could access the school name "some COLLEGE NAME I WANT TO GET" with the following:
json.education[2].school.name
If you know where that element is, then you can just select it as already mentioned by calling
var obj = FACEBOOK_ACTION;
obj.education[2].school.name
If you want to select specifically the last element, then use something like this:
obj.education[ obj.education.length - 1 ].scool.name
Try this,
if (myData.hasOwnProperty('merchant_id')) {
// do something here
}
where JSON myData is:
{
amount: "10.00",
email: "someone#example.com",
merchant_id: "123",
mobile_no: "9874563210",
order_id: "123456",
passkey: "1234"
}
This is a simple example for your understanding. In your scenario of nested objects, loop over your JSON data and use hasOwnProperty to check if key name exists.

Dropdown in kendo UI grid

I need a drop down for a Kendo-UI grid, and came across this example:
http://codepen.io/jordanilchev/pen/cnkih?editors=001
But in this example, both the key and the display text for the drop down are included in the data source of the grid as well, which seems very redundant. I looked at a similar example on Telerik's site, and it was the same there.
Here is the data source for the Type drop-down:
var types = [
{
"Type": "FB",
"Name": "Facebook"
},
{
"Type": "TW",
"Name": "Twitter"
},
{
"Type": "YT",
"Name": "YouTube"
},
{
"Type": "PI",
"Name": "Pinterest"
}
];
So far so good. But here is the data for the actual grid - notice how it also contains both Type and Name for every record:
var products = [{
"ProductID": 1,
"ProductName": "Chai",
"Type": {
"Type": "FB",
"Name": "Facebook"
}
}, {
"ProductID": 2,
"ProductName": "Chang",
"Type": {
"Type": "FB",
"Name": "Facebook"
}
}...
What I had expected is that only the Type would have to be in the data source of the grid - like this:
var products = [{
"ProductID": 1,
"ProductName": "Chai",
"Type": "FB",
}, {
"ProductID": 2,
"ProductName": "Chang",
"Type": "FB",
}...
Is there a way to use a drop-down in the Kendo UI grid without having to include both the key and the display text for every record in the data source of the grid? In other words, the grid would know to reference the datasource of the drop-down to get the display text for the cell.
Update 9/23/2014:
The solution proposed by CodingWithSpike works fine when the datasource for the drop down is a hard-coded / local array, but I am having difficulties getting it to work when loading the data for the drop down from a server. The issue seems to be that the grid gets initialized before the data source for the drop down has been read.
To "simulate" an $http call to populate the data source, I use a setTimeout:
$(document).ready(function () {
var categories = [];
setTimeout(function() {
categories = [{
"value": 1,
"text": "Beverages"
},{
"value": 2,
"text": "Condiments"
},{
"value": 3,
"text": "Confections"
}];
$('#grid').data('kendoGrid').dataSource.read(); // Just as a test, but not even this helps
$('#grid').data('kendoGrid').refresh(); // Just as a test, but not even this helps
}, 1000);
When the data is loaded as above (or via $http), the drop down fields now contain the value (id) instead of the text. Here is a plunker that shows this:
http://plnkr.co/edit/DWaaHGVAS6YuDcqTXPL8?p=preview
Note that the real app is an AngularJs app, and I would rather not use some jQuery hack to wait until the drop down data is available and then create the grid element.
How do I get this working with data from a server?
Take a look at the Kendo demo for "Foreign Key" columns. I think it is exactly what you want.
http://demos.telerik.com/kendo-ui/grid/foreignkeycolumn
They use a list of categories:
var categories = [{
"value": 1,
"text": "Beverages"
},{
"value": 2,
"text": "Condiments"
},{
"value": 3,
"text": "Confections"
},{
"value": 4,
"text": "Dairy Products"
},{
"value": 5,
"text": "Grains/Cereals"
},{
"value": 6,
"text": "Meat/Poultry"
},{
"value": 7,
"text": "Produce"
},{
"value": 8,
"text": "Seafood"
}];
The demo is a little deceiving because their data for the grid contains the entire "Category":
var products = [{
ProductID : 1,
ProductName : "Chai",
SupplierID : 1,
CategoryID : 1,
QuantityPerUnit : "10 boxes x 20 bags",
UnitPrice : 18.0000,
UnitsInStock : 39,
UnitsOnOrder : 0,
ReorderLevel : 10,
Discontinued : false,
Category : {
CategoryID : 1,
CategoryName : "Beverages",
Description : "Soft drinks, coffees, teas, beers, and ales"
}
}
However, if you look at the column definition:
{ field: "CategoryID", width: "200px", values: categories, title: "Category" },
The specified field is CategoryID not Category so the grid data item actually doesn't need to specify a "Category" property at all, and could just be:
var products = [{
ProductID : 1,
ProductName : "Chai",
SupplierID : 1,
CategoryID : 1, // <-- this is the important part!
QuantityPerUnit : "10 boxes x 20 bags",
UnitPrice : 18.0000,
UnitsInStock : 39,
UnitsOnOrder : 0,
ReorderLevel : 10,
Discontinued : false
}
I suspect the "Category" was in there just because this JSON file is shared by a few examples, so a different one may have needed it.
Update
Regarding the issue of the Grid loading before the "Category" (or whatever) FK table:
Use a deferred or the callback on the grid datasource to wait until the FK datasource is done loading before populating the grid data. Alternatively, you can init the grid, but set it to autoBind: false so that it doesn't actually read from its DataSource immediately.
Something like this (sorry for any errors, typing this off the top of my head):
(function () {
// for the foreign keys
var categoriesDataSource = new kendo.data.DataSource({
transport: {
read: {
url: "http://somewhere.com/categories"
}
}
});
// for the actual grid data
var gridDataSource = new kendo.data.DataSource({
...
});
// init the grid widget
var gridWidget = $("#grid").kendoGrid({
dataSource: gridDataSource,
autoBind: false, // <-- don't read the DataSource. We will read it ourselves.
columns: [ ... ]
});
// now we can read the FK table data.
// when that completes, read the grid data.
categoriesDataSource.fetch(function () {
gridDataSource.fetch();
});
});
I asked Telerik about this, and here is the solution they gave.
When the drop-down's data is available, use setOptions on the grid, like this (again, I use setTimeout instead of an Ajax call here, for simplicity):
setTimeout(function() {
categories = [{
"value": 1,
"text": "Beverages"
},{
"value": 2,
"text": "Condiments"
},{
"value": 3,
"text": "Confections"
}];
var grid = $('#grid').data('kendoGrid');
var cols = grid.columns;
cols[1].values = categories;
grid.setOptions({columns: cols});
$('#grid').data('kendoGrid').refresh();
}, 200);
Also, autoBind: false is not needed.
Here is an updated plunker:
http://plnkr.co/edit/ZjuK9wk3Zq80yA0LIIWg?p=preview

Setting selected item in angularjs select directive from a different source

I'm trying to create a form with Angular JS that lets you edit a venue (address, city etc).
The backend runs Django and is available through a REST API (Django Rest Framework) which I'm talking to via Restangular services. This all works fine.
For most form elements, things are pretty standard. I have a venue object and just populate the items with, for example:
<input type="text" ng-model="venue.street">
However, every venue object has a category which, on the backend, is a foreign key to a category object, so in Django this would be:
category = models.ForeignKey(Category)
Now, when getting a venue through the REST API, the category is just referenced by the pk/id of the category object. So, for example, this would be:
{
"id": 14,
"name": "test place",
"slug": "test-place",
"country": "Ireland",
"city": "Dublin",
[...etc...]
"category": 1
},
And a separate REST endpoint, gets me the categories:
[
{
"id": 1,
"name": "Rock"
},
{
"id": 2,
"name": "Classic"
},
{
"id": 3,
"name": "Jazz"
}
]
My problem is that when editing the venue, I would like the drop down menu for categories to show categories by name but just feed the category ID to the backend, as well as pre-select the venue's current category when first shown.
I'm new to Angular but, as far as I can understand, Angular populates a select directive with references to the object itself, not something simple like an ID.
At the moment I have this:
<select ng-model="venue.category" ng-options="category.name for category in categories track by category.id"></select>
Obviously, this doesn't work as, even though venue.category is just a number, and category.id also, they are not the same object.
I'm probably missing an obvious solution here but would appreciate any pointers.
Thanks!
Track by is for when you want two different objects to be equal in regards to the select box. This isn't the case. You just want the model to be the id itself. Angular supports this like so.
http://jsfiddle.net/XLpFN/
Example:
<select ng-model="venue.category" ng-options="category.id as category.name for category in categories"></select>
$scope.categories = [
{"id": 1, "name": "Rock"},
{"id": 2, "name": "Classic"},
{"id": 3, "name": "Jazz"}
];
$scope.venue = {
"id": 14,
"name": "test place",
"slug": "test-place",
"country": "Ireland",
"city": "Dublin",
"category": 2
};

Handsontable Replace autocomplete values with key before posting

I am using HandsOnTable to make editing database tables more interactive on my site.
HandsOnTable fulfils nearly all my requirements except that some columns in my database actually store foreign keys rather than local string values.
In the UI I would like these columns to appear as dropdown menus where the user selects a readable value mapped to the previously mentioned foreign key (I.e. something like an HTML name/value select).
Unfortunately HandsOnTable does not have such a cell type. The closest thing to it is autocomplete. This allows me to create a dropdown, but it only contains values; no corresponding keys. Here is how it is created:
"source": ["Jebediah", "Bob", "Bill", "Buzz"]
So what I am planning is to send two Json strings from the server:
One containing the parameters needed by HandsOnTable to render the table:
{
"data": [
{ "ID": 1, "Description": "Crude", "Volume": 204, "Customer": "jebediah" },
{ "ID": 2, "Description": "Hidrogen", "Volume": 513, "Customer": "Bob" },
{ "ID": 3, "Description": "Coal", "Volume": '67', "Customer": "Bill" },
{ "ID": 4, "Description": "Wood", "Volume": '513', "Customer": "Buzz" }
],
"columns": [
{ "data": "ID", "type": "numeric" },
{ "data": "Description", "type": "text"},
{ "data: "Volume", "type": "numeric" },
{ "data": "color", "type": "autocomplete", "strict": "true",
"source": ["Jebediah", "Bob", "Bill", "Buzz"]}
]
}
The second mapping keys to values
{
"mappings": [
{"key": 0, "value": "Jebediah"},
{"key": 0, "value": "Bob"},
{"key": 0, "value": "Bill"},
{"key": 0, "value": "Buzz"}
]
}
So far so good. Now for the tricky part:
HandsOnTable has a function (getData()) that allows me to retrieve the tables data as a Json string ready to be sent back to the server:
var jdata = myHandsOnTable.getData();
Where jdata would look something like this:
"data": [
{ "ID": 1, "Description": "Crude", "Volume": 204, "Customer": "jebediah" },
{ "ID": 2, "Description": "Hidrogen", "Volume": 513, "Customer": "Bob" },
{ "ID": 3, "Description": "Coal", "Volume": '67', "Customer": "Bill" },
{ "ID": 4, "Description": "Wood", "Volume": '513', "Customer": "Buzz" }
]
Now before posting, I would like to replace that values for the Customer node with their matching pair key within the mappings json string.
How can I best achieve this in JavaScript/JQuery?
Is there a function that works something as follows?:
jdata.replaceNode('node', mappings)
Thanks
I had a similar issue and here's what I did...
For each foreign key column, I stored 2 values in handsontable; one for the id itself, which I set as a hidden column and the other is the user friendly readable text value as dropdowns.
Everytime the value of a dropdown is changed, I also change the corresponding hidden id. In my case I have a dropdown outside the handsontable as a filter which I use to map key/value pairs, but you could use Hashtables or anything else.
Now the code...
Handsontable config:
afterChange: function (changes, source) { AfterChange(changes, source); }
After change event (called everytime there is a change in the table):
function AfterChange(Changes, Source) {
if (Source === 'loadData') {
return; //don't save this change
}
var rowIndex = 0, columnID = 1, oldTextVal = 2, newTextVal = 3, ntv = '', nv = '';
$(Changes).each(function () {
if (this[columnID] === 'CategoryID') {
// Do nothing...
//To make sure nothing else happens when this column is set through below
}
else if (this[columnID] === 'CategoryName') {
ntv = this[newTextVal];
//This is where I do my mapping using a dropdown.
nv = $('#CategoriesFilterDropdown option').filter(function () { return $(this).text() === ntv; }).val();
//13 is my CategoryID column
$container.handsontable('setDataAtCell', this[rowIndex], 13, nv);
}
});
}
}
This way, you change the foreign keys as you and don't need to loop through it all before saving. It also makes it easy to send the table data as is back to server.
In summary,
The user interacts with CategoryName column (which is of type autocomplete).
The CatgoryID column is hidden to the user by setting the column width to 0 using the colWidths option of handsontable.
When the CategoryName field changes, use afterChange event to set the corresponding CategoryID column. In my case, I use a dropdown somewhere else on the page to map Name => ID, but you can use other means such as a hashtable.
I hope it makes sense...

Categories