I'm working on a simple web page using knockout. Consider I have the following ViewModel function
self.sample_data = ko.observableArray([
{
1:[
{ title1:"abc", title2:"def"},
{ title1:"abc", title2:"def"}
],
2:[
{ title1:"ghi", title2:"jkl"},
]
}
]);
Am able to bind the specific json value with key value as '1' to the view layer as follows.
<h1 data-bind="text:sample_data[0].1[0].title1"></h1>
Consider I have two buttons 'previous' and 'next'. On clicking the button 'next' I should bind the data associated with key value as '2' in the JSON to the same h1 tag. How can we acheive that?
You'll want an observable index variable in your view model. Clicking the next button would increment that variable:
var self = {};
self.sample_data = ko.observableArray([
{
"1": [
{ title1: "abc11", title2: "def11" },
{ title1: "abc12", title2: "def12" }
],
"2": [
{ title1: "ghi21", title2: "jkl21" }
]
}
]);
self.index = ko.observable(1);
self.goToNext = function() {
self.index(self.index() + 1);
};
Then, your data binding could look something like this:
<h1 data-bind="text: sample_data()[0][index()][0].title1"></h1>
<a data-bind="click: goToNext">Next</a>
Your Json object has invalid key:
JSON only allows key names to be strings
Here's the binding sample
Html
<h1 data-bind="text: sample_data()[0][2][0].title1"></h1>
JavaScript
var self = {};
self.sample_data = ko.observableArray([
{
"1":[
{ title1:"abc11", title2:"def11"},
{ title1:"abc12", title2:"def12"}
],
"2":[
{ title1:"ghi21", title2:"jkl21"},
]
}]);
ko.applyBindings(self);
You can play with code here
Related
Got a question about Angularjs and it's ng-repeat/ng-click workings.
So on this system that I'm working on there is a lot of code re-used for datatables, and I'm trying to make a generic template/service to remedy this. Now I'm running into a problem where we have multiple buttons with their own function calls on being clicked.
I've so far got this setup:
My column object defined as so:
var columns = [
{
identifier: "id",
type: "text"
},
{
identifier: "type",
type: "text"
},
{
identifier: "label",
type: "text"
},
{
identifier: "actions",
type: "button",
multi: true,
content: [
{
icon: "fa-globe",
events: {
click: $scope.openMapModal
}
},
{
icon: "fa-list",
events: {
click: $scope.openGroupModal
}
}
]
}
];
And my HTML as following:
<tr ng-repeat="row in table.data" ng-model-instant>
<td ng-repeat="column in table.columns" ng-if="column.type === 'text'">
{{TableService.getByString(row, column.identifier)}}
</td>
<td ng-repeat="column in table.columns" ng-if="column.type === 'button' && column.multi">
<a ng-repeat="button in column.content" class="btn fa {{button.icon}}" ng-click="button.events.click(row)"></a>
</td>
</tr>
Just for completeness, my TableService.getByString and a small table data set:
(note that columns defined above is set by a function into the table object, and I did not include it in the object).
var table = {
data: [
{
id: 0,
label: "foo",
type: "bar"
},
{
id: 1,
label: "one",
type: "bar"
},
{
id: 2,
label: "foo",
type: "two"
}
]
}
function getKeyObj(obj, key) {
var retVal = {
"key": key,
"obj": obj
};
if (retVal.key.indexOf('.') > -1) {
var keyParts = retVal.key.split('.');
retVal.key = keyParts.pop();
while (keyParts.length && (obj = obj[keyParts.shift()])) ;
retVal.obj = obj;
}
return retVal;
}
function getByString(obj, key) {
var ret = getKeyObj(obj, key);
return ret.obj[ret.key];
}
Now the problem that I'm encountering is that my functions are not being called in my ng-click's whenever I click on the buttons.
I've also tried it with settings the function as a string in my column object, but it didn't work either.
Am I going in the right direction with this or do I need to rethink my generalization, and ifso, what alternative is there?
Are you sure $scope.openMapModal is defined at the time you define the columns array? Could it be that you are assigning undefined to the click property instead of a reference to the function?
I'm trying update a textbox when I change the value of a select.
Now, I know this is fairly simple however I'm not looking to update the textbox with easy to get data.
Here's what I can do so far:
Catch the change event
Get the ID from the Select dropdown
Here's what I want to do:
Use the selected ID to return a property value from inside an array of objects inside my viewmodel. However, the ID does not match the array index (i.e. the selected ID may be "43" but the index is 0).
Not much point in posting my Knockout code as it's fairly basic so instead I'll post my VM structure.
ViewModel
--> Property1
--> Property2
--> Array
--> Object[0]
--> "Property to match with the selected ID"
--> "Property that I want to return"
--> Object[1]
Not really sure how much sense this is making, hope it makes some.
Any other info can be provided.
Thanks!
EDIT
VM
var PurchaseOrderViewModel = function (data) {
var self = this;
self.UpdateCurrency = function (data, event) {
//
}
self.UpdateSupplierContactDetails = function (data, event) {
//
}
ko.mapping.fromJS(data, {}, self);
}
$(document).ready(function () {
var viewModel = new PurchaseOrderViewModel(#Html.Raw(jsonString));
ko.applyBindings(viewModel);
});
EDIT #2
Managed to get a working solution, in case anyone else has issues here is how I worked around it.
var contact = ko.unwrap(ko.utils.arrayFirst(self.AllSupplierContacts(),
function (item) {
return ko.unwrap(item.Id) === newID;
}).BusinessTelephoneNumber);
This answer has two parts: the code I think you're looking for (1), and the code I think you should write (2).
1. Finding an item that matches an id
ko.applyBindings(new function() {
this.ids = ["A", "B", "C", "D"];
this.selectedId = ko.observable();
this.items = [
{ key: "A", value: 1 },
{ key: "B", value: 2 },
{ key: "C", value: 3 },
];
this.selectedValue = ko.pureComputed(function() {
var selectedId = this.selectedId();
// Find the object in items of which the property
// `key` matches the `selectedId` and return it
var match = this.items.find(function(item) {
return item.key === selectedId;
});
return match ? match.value : "No item found";
}, this);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: ids, value: selectedId"></select>
<div>
Your selected value: <strong data-bind="text: selectedValue"></strong>
</div>
2. Binding the actual elements to the select:
Knockout's options binding does many things out of the box. You probably don't need to store ids and items separately. By telling the binding which property it should render in the dropdown (optionsText), and which to store as a value (optionsValue), you'll need a lot less code to do the same thing:
ko.applyBindings(new function() {
this.items = [
{ key: "A", value: 1 },
{ key: "B", value: 2 },
{ key: "C", value: 3 },
{ key: "D", value: 4 }
];
this.selectedValue = ko.observable();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: items,
optionsText: 'key',
optionsValue: 'value',
value: selectedValue"></select>
<div>
Your selected value: <strong data-bind="text: selectedValue"></strong>
</div>
The whole issue of "having to look up an ID in an array" can be side-stepped by not working with IDs at all, but with the array items themselves.
function MyList(params) {
var self = this;
// observables
self.items = ko.observableArray();
self.selectedItem = ko.observable();
// init
ko.mapping.fromJS(params, {}, self);
// post-init
self.items.sort(function (a, b) {
return ko.unwrap(a.key) < ko.unwrap(b.key) ? -1 : 1;
});
}
ko.applyBindings(new MyList({
items: [
{ key: "D", id: 4 },
{ key: "A", id: 3 },
{ key: "B", id: 2 },
{ key: "C", id: 1 }
]
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<select data-bind="
optionsCaption: 'Please select...',
options: items,
optionsText: 'key',
value: selectedItem
"></select>
<div data-bind="with: selectedItem">
Your selected value: <strong data-bind="text: id"></strong>
</div>
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
I want to display data in a JS Heat map like this one:
This is the 2 diffrent types of data I'am receiving:
Im getting the data like this:
public function index() {
$regions = DB::table('locations')
->select('regionCode', DB::raw('count(id) as total'))
->groupBy('regionCode')
->get();
$newRegions = [];
foreach( $regions as $region ){
$newRegions[$region->regionCode] = $region->total;
}
return view('admin.dashboard-v2', compact('newRegions'));
}
And this is how I'm SUPPOSED to display it:
var map = AmCharts.makeChart( "chartdiv", {
type: "map",
"theme": "light",
colorSteps: 10,
dataProvider: {
map: "usaLow",
areas: [ {
id: "US-AL",
value: 4447100
}, {
id: "US-AK",
value: 626932
}, {
id: "US-AZ",
value: 5130632
}, {
id: "US-AR",
value: 2673400
}, {
id: "US-CA",
value: 33871648
},.... and so on
Im having trouble displaying it like above: How would I display the data im getting from the array into the 'areas' section in the ChartJS script?
I tried this, but it dosen't work:
areas: [ {
id: "US-{!! json_encode(array_keys($newRegions)) !!}",
value: {!! json_encode(array_values($newRegions)) !!}
} ]
is is a very common problem, transforming data structures to specific implementations.
the phpleague has many cool packages to help you with this.
one of my favorites is
http://fractal.thephpleague.com/
However, I want to show you in plain PHP how to transform an array into the desired structure.
1) get the array of data that you need to transform (the data that you showed in the previous image)
array:15[
0 => regioncode:"AL"
total: 16]
]...
2) transform the array using the array_map function
//http://php.net/manual/en/function.array-map.php
$transformedarray = array_map(function ($loopdata) {
return [
"id" => "US-".$loopdata['regionCode'],
"value" => $loopdata['value']
];
}, $regions->toArray());
3) var_dump($transformedarray) o return this array with laravel responde to check the desired structure match the one you require.
4) pass this variable (array) to the view using this method
return view('admin.dashboard-v2')->with(['newarray' => $transformedarray]);
5) if you are using BLADE try to user control structures to loop over your data
https://laravel.com/docs/5.0/templates
insert this code sniplet where you need the data to populate in the view
areas: [
#foreach ($newarray as $newarr)
{
id:{{ $newarr->id }},
value:{{ $newarr->value }}
},
#endforeach
]
Hope this helps
This is how I did:
$heatMap = DB::table('locations')
->select('regionCode', DB::raw('count(id) as total'))
->groupBy('regionCode')
->get();
$newHeatMap = [];
foreach( $heatMap as $regionH ){
$newHeatMap[] = [ 'id' => 'US-' . $regionH->regionCode, 'value' => $regionH->total ];
}
return view('admin.dashboard-v2', compact('newHeatMap'));
Then In my Chart JS
dataProvider: {
map: "usaLow",
areas: {!! json_encode(array_values($newHeatMap)) !!}
},
I have an object which contains an array of objects called "blocks":
$scope.microsite = {
images: [
{url: "https://unsplash.it/800/400/?image=20"},
{url: "https://unsplash.it/800/400/?image=15"},
{url: "https://unsplash.it/800/400/?image=52"}
],
blocks: []
};
When I add stuff to this array, it behaves perfectly normally:
$scope.addElement = function(a){
if(a=='heroslider'){
var data = {
slides: [
{
id:0,
image:0,
title: "Title",
desc: "Description",
},
{
id:1,
image:1,
title: "Title",
desc: "Description",
},
{
id:2,
image:2,
title: "Title",
desc: "Description",
}
]
};
} else if(a=='threecol'){
var data = {
columns: [
{
title: "Column one",
text: "This is a column for features",
},
{
title: "Column two",
text: "This is a column for features",
}
]
};
}
var element = {
template: a,
data: data
};
$scope.microsite.blocks.push(element);
}
However when I try to remove an object from the array by calling this function on ng-click and passing in the object from an ng-repeat...
$scope.removeElement = function(element){
var x = $scope.microsite.blocks.indexOf(element);
console.log($scope.microsite.blocks[x]);
console.log(x);
$scope.microsite.blocks.splice(x, 1);
}
I am able to get both the correct object and the correct index in my console, but when it goes to splice the array, the last object is always being deleted which is very strange as this should only be happening when the index I'm trying to delete doesn't exist (and therefore would equal -1)
Any ideas why this could be happening?
EDIT: I have also tried using ng-click="microsite.blocks.splice($index, 1)" directly in the element, as well as passing the $index into the function instead of the element. In all cases, the correct index is found, but the result is still the same, only the last entry is ever deleted.
Turns out this was an error with "track by $index" in Angular. After removing "track by $index" from my ng-repeat, splice() functioned normally.
I'm currently struggling with implementing a jQuery plugin to my site for tags (with custom data objects) with autocomplete. jQuery TextExt can be found here (http://textextjs.com/). I'm currently struggling with using custom data objects for each tag, which can only be chosen from what autocompletes. Based on this example (http://textextjs.com/manual/examples/tags-with-custom-data-objects.html) I'm trying to figure out how to return both "name" and "id" when a tag is chosen. Does anyone know how to achieve this or point me in the correct direction?
Perhaps the answer is somewhere in this example (http://textextjs.com/manual/examples/filter-with-suggestions.html)?
Here's what I have written, which isn't working (it only returns the name, I've tried adding 'item.id' to the functions but that didn't work for me either):
<script type="text/javascript">
jQuery(document).ready(function( $ ){
jQuery('#textarea').textext({
plugins: 'tags',
items: [
{ name: 'PHP', id: '1' },
{ name: 'Closure', id: '2' },
{ name: 'Java', id: '3' }
],
ext: {
itemManager: {
stringToItem: function(str)
{
return { name: str };
},
itemToString: function(item)
{
return item.name ;
},
compareItems: function(item1, item2)
{
return item1.name == item2.name;
}
}
}
});
})
</script>
Your itemManager code should probably look like this, you will need to store the suggestions in a custom array to look up their relevant ids in the stringToItem Method
itemManager: {
items: [], // A custom array that will be used to lookup id
stringToItem: function(str)
{
//Lookup id for the given str from our custom array
for (var i=0;i<this.items.length;i++)
if (this.items[i].name == str) {
id = this.items[i].id;
break;
}
return { name: str, id: id };
},
itemToString: function(item)
{
//Push items to our custom object
this.items.push(item);
return item.name ;
},
compareItems: function(item1, item2)
{
return item1.name == item2.name;
}
}