I am using the knockout simple grid found here:
http://knockoutjs.com/examples/grid.html
I want to be able to add a select into the grid, which has a data-bind attribute assigned to an object array in my vm.
So I have added another column from the example:
this.gridViewModel = new ko.simpleGrid.viewModel({
data: this.items,
columns: [
{ headerText: "Item Name", rowText: "name" },
{ headerText: "Sales Count", rowText: "sales" },
{ headerText: "Price", rowText: function (item) { return "$" + item.price.toFixed(2) } },
*{ headerText: "Select", rowText: function (item) { return "<select data-bind=\"options:items, optionsText: 'name', optionsValue: 'name'\"></select>" } }*
],
pageSize: 4
});
And changed the text attribute to html within the control:
<td data-bind=\"*html*: typeof rowText == 'function' ? rowText($parent) : $parent[rowText] \"></td>\
The Selects appear, but not populated with data from my object array.
JSFiddle found here:
http://jsfiddle.net/vwj2p/1/
(I have pasted in the code from the simple grid above as I made a change to the simplegrid code).
{ headerText: "Select", rowText: function (item) { return "<select data-bind=\"options:$root.items, optionsText: 'name', optionsValue: 'name'\"></select>" } }
I'm assuming each item object doesn't have an items property and you're trying to reference the viewModel's items array? If so, change your code to the above.
This still won't work, however. If you look at the html binding documentation, you'll see that it's only going to spit out static html. During the binding process when this is all getting rendered, KO doesn't applyBindings to the generated HTML.
I tried playing around with the code a bit to try and do ko.applyBindingsToDescendants(viewModel, {td element}), where {td element} is a reference to the parent element with the html binding on it, when the items observableArray updated but that didn't seem to do anything.
Bottom line, I don't think you're going to get this to work without doing a lot of plumbing work to the simpleGrid. It is just a simple grid, after all.
Related
I am trying to access the option elements from a select2 dropdown list using a Knockout custom binding in order to disable some of them (some of the options). The custom binding is:
ko.bindingHandlers.select2 = {
after: ["options", "value"],
update: function (el, valueAccessor, allBindingsAccessor, viewModel) {
var allBindings = allBindingsAccessor();
var select2 = $(el).data("select2");
}
};
and the HTML part is:
<div style="width: 350px;">
<select style="width: 100%;" data-bind="value: attributiSelezionati, options: data, valueAllowUnset: true, optionsText: 'text', optionsValue: 'id', select2: { placeholder: 'Select an option...', allowClear: true, multiple: true}"></select>
</div>
where the data array is:
this.data = ko.observableArray([]);
this.data.push(new Item(1, "Item 1"));
this.data.push(new Item(2, "Item 2"));
this.data.push(new Item(2, "Item 22"));
this.data.push(new Item(3, "Item 3"));
this.data.push(new Item(null, "Item 4"));
class Item {
id: KnockoutObservable<number> = ko.observable<number>();
text: KnockoutObservable<string> = ko.observable<string>();
constructor(Id: number, Text: string) {
this.id(Id);
this.text(Text);
}
}
I can see the data when I hover over the el element but I do not know how to access it programmatically. Does anyone know how to get these items?
It should be in valueAccessor()
valueAccessor: This represents a JavaScript function that can be used to access the current property or expression involved in this binding. Because Knockout allows you to use either a view model property directly (such as "data-bind='enabled: isEnabled'"), or an expression (such as "data-bind='enabled: firstName.length > 0'"), there's a utility function that will "unwrap" and give you the actual value used in the binding. Incidentally, this function is called "ko.unwrap."
var val = ko.unwrap(valueAccessor());
//val.options;
I have a problem how to call onchanges knock js to my select option, i already have a function and html, but when i choose the select option, nothing changes
<select data-bind="event:{change:setSelectedStation() },
options: seedData,
optionsText: 'text',
optionsValue: 'value'">
</select>
here is my function
setSelectedStation: function(element, KioskId){
this.getPopUp().closeModal();
$('.selected-station').html(element);
$('[name="popstation_detail"]').val(element);
$('[name="popstation_address"]').val(KioskId);
$('[name="popstation_text"]').val(element);
// console.log($('[name="popstation_text"]').val());
this.isSelectedStationVisible(true);
},
Use knockout's two-way data-binds instead of manually subscribing to UI events.
Knockout's value data-bind listens to UI changes and automatically keeps track of the latest value for you.
Instead of replacing HTML via jQuery methods, you use text, attr and other value bindings to update the UI whenever your selection changes.
If you want to perform additional work when a selection changes (e.g. closing a pop up), you subscribe to the selected value.
var VM = function() {
this.seedData = [
{
text: "Item 1",
data: "Some other stuff"
},
{
text: "Item 2",
data: "Something else"
},
{
text: "Item 3",
data: "Another thing"
}
];
this.selectedItem = ko.observable();
this.selectedItem.subscribe(function(latest) {
console.log("Input changed");
}, this);
};
ko.applyBindings(new VM());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<select data-bind="
value: selectedItem,
options: seedData,
optionsText: 'text'">
</select>
<!-- ko with: selectedItem -->
<p>
Your selection: <span data-bind="text: data"></span>
</p>
<!-- /ko -->
I've been reading a lot on angular scopes and inheritance but I can't get my head around this problem. Here is the HTML I'm using:
<div class="sensorquery-sensor" ng-repeat="sensor in query.sensors" ng-controller="SensorsCtrl">
<select class="form-control"
ng-model="selected.sensor"
ng-options="sensor.name for sensor in parameters.sensors">
</select>
<select class="form-control"
ng-model="selected.definition"
ng-options="definition.value for definition in definitions">
</select>
<select class="form-control"
ng-model="selected.operation"
ng-options="operation for operation in operations">
</select>
</div>
As you can see, I have an ng-repeat based on query.sensors. The values stored in this query.sensors array should be simple:
{
name: 'sensor1',
type: 'temperature'
}
But I want to use a child controller: SensorsCtrl to handle more logic per sensor and hide the complexitiy of sensors. A sensor can look like:
{
name: 'sensor1',
attributes: [
'model',
'brand'
],
definitions: [
{
datatype: 'double',
value: 'temperature'
},
{
datatype: 'integer',
value: 'pressure'
},
{
datatype: 'string',
value: 'color'
}
]
}
So it's in my SensorsCtrl controller where I want to put the selection logic:
$scope.$watch('selected.sensor', function(sensor) {
$scope.definitions = sensor.template.definition;
});
$scope.$watch('selected.definition', function(definition) {
if (definition.datatype === 'string') {
$scope.operations = ['Count'];
} else {
$scope.operations = ['Max', 'Min'];
}
$scope.selected.operation = _.first($scope.operations);
});
How do I keep the link with the parent query.sensors[$index] while transforming the sensor as the user selects different sensors and definitions?
Setting up a watcher on selected and updating the query.sensors array triggers an infinite $digest loop.
I found the solution which was right before my eyes:
<div class="sensorquery-sensor" ng-repeat="sensor in query.sensors" ng-controller="SensorsCtrl">
<!-- ... -->
</div>
The sensor is a reference to the original object of the parent query.sensors. An it's created in the scope of the sub-controller.
So in my SensorsCtrl controller, I can just watch:
$scope.$watch('sensor.definition', function(definition) {
/* ... */
});
So I can put hide some complexity in this controller while maintaining a proper link to the original element.
It does not answer the question of maintaining a less complex object but it's a different question I guess.
I have a drop down list that is bound to a SelectedFormat value. The lists options are populated from external source on load and matches the view models data.Format object base on id.
Take a look at the js fiddle
Can anyone tell me why the model updates but the UI is not updating with the correct Format.Name
Thanks.
HTML:
<div data-bind="text:data.Format.Name"></div>
<select data-bind="
options:Controls,
optionsText: 'Name',
value: data.SelectedFormat"></select>
Model:
var jsonData = {
Id: "abc-123",
Name: "Chicken Cheese",
Format: {
Id: 2,
Name: 'Medium',
Other: 'Bar'
}
};
var self = this;
self = ko.mapping.fromJS(data);
self.SelectedFormat = ko.observable(
//return the first match based on id
$.grep(vm.Controls,function(item){
return item.Id === self.Format.Id();
})[0]
);
//when changed update the actual object that will be sent back to server
self.SelectedFormat.subscribe(function (d) {
this.Format = d;
},self);
In your code, you have Format and SelectedFormat. The former isn't an observable and so can't trigger updates. You have to use SelectedFormat instead.
<div data-bind="text:data.SelectedFormat().Name"></div>
Example: http://jsfiddle.net/QrvJN/9/
In knockout.js, is it possible to let the right-hand-side of a binding (the value of the binding) be dynamic? For example,
<input data-bind="value: dynamicBinding()"/>
<script type="text/javascript">
var vm = {
dynamicBinding : function() {
return "foo().bar";
},
foo : ko.observable({
bar : ko.observable("hi");
}
};
ko.applyBindings(vm);
</script>
the result should be that the the dynamicBinding function is executed while applying the bindings and the resulting string is used as the binding. The input element should be bound to foo().bar, which is the observable with the value "hi".
If you wonder why I would want this, I am trying to render a dynamic table with knockout, where both the rows and the columns are observableArrays, and I want to allow the column definitions to contain the expression of the binding for that column. I.e., I want to be able to do this:
<table data-bind="foreach: data">
<tr data-bind="foreach: $root.columns">
<td data-bind="text: cellValueBinding()"></td>
</tr>
</table>
<script type="text/javascript">
var vm = {
data: ko.mapping.fromJS([
{title: "Brave New World", author: { name : "Aldous Huxley" },
{title: "1984", author: { name : "George Orwell" },
{title: "Pale Fire", author: { name : "Vladimir Nabokov" }]),
columns: ko.observableArray([
{header: "Title", cellValueBinding: function () { return "$parent.title"; }},
{header: "Author", cellValueBinding: function () { return "$parent.author().name"; }}
])
};
ko.applyBindings(vm);
</script>
As you can see from the example, the column definition knows how to extract the value from the data. The table markup itself is more or less a placeholder. But as far as I can tell, this does not work, due to the way knockout processes the bindings. Are there any other options available?
Thanks.
Solution: I ended up using Ilya's suggestion - I can let cellValueBinding be a function that accepts the row and column as arguments, and returns an observable. This technique is demonstrated in this fiddle.
Use ko.computed for it.
Look on example
JSFiddle
EDIT
In your second example, you can pass $parent value ti the function
<td data-bind="text: cellValueBinding($parent)"></td>
and in model
{header: "Title", cellValueBinding: function (parent) { return parent.title; }},
{header: "Author", cellValueBinding: function (parent) { return parent.author().name; }}
JSFiddle