knockoutjs subscribe not firing - javascript

I have the following client-side code:
var ProfileManager = function () {
self.SelectedLanguage = ko.observable();
self.SelectedLanguage.subscribe(function (newValue) {
alert("The person's new name is " + newValue);
});
var bindUIwithViewModel = function (viewModel) {
ko.applyBindings(viewModel);
};
}
and I later do bindUIwithViewModel(self);.
and a HTML select bound to SelectedLanguage:
<select id="selectAvailableLanguages" class="form-control language-select" data-bind="options: AvailableLanguages, optionsText: 'Code', value : SelectedLanguage"></select>
The select gets populated successfully, the value inside SelectedLanguage observable changes, but the alert just won't show up. Any ideas?
Also, don't know if related but the observe array inside __ko.mapping_ is an array[0]..

Please check the fiddle. Note, "AvailableLanguages" and "SelectedLanguage" properties should exist in the same context, i.e. belong the binded view model.
I've modified your JS code:
var ProfileManager = function () {
self.SelectedLanguage = ko.observable();
self.SelectedLanguage.subscribe(function (newValue) {
alert("The person's new name is " + JSON.stringify(newValue));
});
self.AvailableLanguages = [ { Code: 'c++', Person: 'John' }, { Code: 'c#', Person: 'Mark' } ]
}
ko.applyBindings(new ProfileManager());

Related

Two Select computed Knockout

I try to binding two computed select with Knockout:
- the first select(DropDownLinee) is filled when on page load
- the second select(DropDownCorse) is filled when the user select an item on the first select
This is the example:
<select id="DropDownLinee" data-bind="options: ArrayLinee, optionsText: 'NomeLinea', optionsValue: 'NomeLinea', value: selectedLinea " data-toggle="dropdown"></select>
<select id="DropDownCorse" data-bind="options: ArrayCorse, optionsText: 'CodiceCorsa', optionsValue: 'CodiceCorsa', value: selectedCorsa " data-toggle="dropdown"></select>
function LineeViewModel() {
var self = this;
self.selectedLinea = ko.observable();
self.selectedCorsa = ko.observable();
self.ArrayLinee = ko.observableArray([]);
self.ArrayCorse = ko.observableArray([]);
$.getJSON('/Home/GetLines', function (data) {
self.ArrayLinee(data);
});
self.ArrayCorse = ko.computed(function () {
$.getJSON('/Home/GetRides',
{
LineaSelezionata: self.selectedLinea(),
DirezioneSelezionata: $('input[name=radio4]:checked', '.areaselezione').val()
},
function (data) {
debugger;
self.ArrayCorse(data);
});
});
}
lineeVM = new LineeViewModel();
ko.applyBindings(lineeVM);
I have this error when i check to load the 'DropDownCorse':
Uncaught Error: Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.
Can anyone help me to solve this problem???
Thank in advance greetings Donato
You want to use subscribe, not a computed.
self.selectedLinea.subscribe(function (newSelection) {
$.getJSON('/Home/GetRides',
{
LineaSelezionata: newSelection,
DirezioneSelezionata: $('input[name=radio4]:checked', '.areaselezione').val()
},
function (data) {
debugger;
self.ArrayCorse(data);
});
});

How to bind a default value from observable array to dropdown?

I have a dropdown and data binded to it as below
HTML:
<div>
<label>Notice Type</label>
<select id="ntctype" data-bind="options: NoticeType, value: selectedNoticeType, optionsCaption:'Choose...', optionsValue:'NoticeTypeID', optionsText:'NoticeTypeDescription'"></select>
</div>
KO JS:
self.NoticeType = ko.observableArray([]);
self.selectedNoticeType = ko.observable();
$.getJSON("GetNoticeType", null, function (data) {
self.NoticeType(data);
}
);
the NoticeType array looks like this
[{"NoticeTypeID":1,"NoticeTypeDescription":"Close"},{"NoticeTypeID":2,"NoticeTypeDescription":"Open"}]
I would like to set the deafult value as Close after binding.I tried using optionsAfterRender & ko.applybindingstoNode none of them worked.
What would be the clean and neat approach to do this?
As mentioned in the comments you just need to set default value to value binded observable in viewModel
View Model :
function accountViewModel() {
var self = this;
self.NoticeType = ko.observableArray();
self.selectedNoticeType = ko.observable();
self.ajaxcall = function () {
self.NoticeType([{
"NoticeTypeID": 1,
"NoticeTypeDescription": "Close"
}, {
"NoticeTypeID": 2,
"NoticeTypeDescription": "Open"
}]);
self.selectedNoticeType = ko.observable(1);
}
self.ajaxcall();
}
Working fiddle here

knockout binding handler - undefined bindingContext.$data on IE9

I am trying to create a custom binding handler to apply a role based access to fields on page.
In custom handler it will check values of other observables from viewModel and based on condition it will enable or disable input control.
But I am not able to get current ViewModel through bindingContext.$parent , $root, $data or $rawData.
when I debug on IE it only displays {...}
The issue occurs only on IE, It works fine on google crome.
Can someone help me on how do I get current ViewModel through bindingContext.
var divForm = document.getElementById('divform');
function AuditFormViewModel() {
self = this;
self.Name = ko.observable("Kiran");
self.Email = ko.observable("kiranparab0#gmail.com");
self.Complete = ko.observable(true);
self.Send = ko.observable(false);
self.conditionArray = [
{ Field: "Name", condition: [{ Property: "Complete", Value: true }, { Property: "Send", Value: true }] },
{ Field: "Email", condition: [{ Property: "Complete", Value: false }] }
];
}
ko.bindingHandlers.hasAccess = {
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var field = valueAccessor();
var accessConditions = [];
accessConditions = bindingContext.$data.conditionArray;
var condition = accessConditions.filter(function (cnd) {
return cnd.Field === field
})[0].condition;
var value = true;
for (var i = 0; i < condition.length; i++) {
var cndProp = condition[i].Property;
var cndVal = bindingContext.$data[cndProp]();
value = value && (condition[i].Value === cndVal);
}
ko.bindingHandlers.enable.update(element, function () { return value });
}
};
auditFormViewModel = new AuditFormViewModel();
ko.applyBindings(auditFormViewModel, divForm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
<div id="divform">
Name : <input type="text" data-bind="value:Name, hasAccess:'Name'" />
<br />
Email : <input type="text" data-bind="value:Email, hasAccess:'Email'" />
</div>
Here is the fiddle
In my code I have not declared self as var,
its just change of line. 'var self = this' instead 'self = this'

Knockout, previous value of select / dropdown

I want to know what the previous value is from a Dropdown list.
In the following html I can set the Options and the currently selected.
<select data-bind="options: group_128,
optionsText: 'description',
value: selectedgroup_128,
event: { change: selectionChanged_group_128 }">
</select>
in the Javascript / viewModel I do :
selectionChanged_group_128 = function (data, event) {
self.addItemClicked2(self.selectedgroup_128, event)
};
At this point I want to know what the previous selected item was to send it to the addItemClicked2.
You could subscribe to the observable to get the value before it changed:
self.selectedgroup_128.subscribe(function(oldValue) {
self.selectedgroup_128.previousValue = oldvalue;
}, null, "beforeChange");
Now you can get the previous value like this:
self.selectedgroup_128.previousValue
Why not just change your model to store the new value as previous value?
http://jsfiddle.net/uLeDP/
var ViewModel = function() {
this.group_128 = ko.observable([{description:"first"},{description:"second"}]);
this.selectedgroup_128 = ko.observable("first");
this.prevgroup_128 = null;
this.selectionChanged_group_128 = function(val) {
alert("Now I'm " + this.selectedgroup_128().description + " but I was " + this.prevgroup_128);
this.prevgroup_128 = this.selectedgroup_128().description;
}
};

subscribing an observableArray to a computed

I have an object that is constructed upon a table row from the database. It has all the properties that are found in that entry plus several ko.computed that are the middle layer between the entry fields and what is displayed. I need them to be able translate foreign keys for some field values.
The problem is the following: One of the properties is an ID for a string. I retrieve that ID with the computed. Now in the computed will have a value that looks like this: 'option1|option2|option3|option4'
I want the user to be able to change the options, add new ones or swap them around, but I also need to monitor what the user is doing(at least when he adds, removes or moves one property around). Hence, I have created an observable array that I will bind in a way that would allow me to monitor user's actions. Then the array will subscribe to the computed so it would update the value in the database as well.
Some of the code:
function Control(field) {
var self = this;
self.entry = field; // database entry
self.choices = ko.observableArray();
self.ctrlType = ko.computed({
read: function () {
...
},
write: function (value) {
if (value) {
...
}
},
owner: self
});
self.resolvedPropriety = ko.computed({
read: function () {
if (self.ctrlType()) {
var options = str.split('|');
self.choices(createObservablesFromArrayElements(options));
return str;
}
else {
return '';
}
},
write: function (value) {
if (value === '') {
//delete entry
}
else {
//modify entry
}
},
deferEvaluation: true,
owner: self
});
self.choices.subscribe(function (newValue) {
if (newValue.length !== 0) {
var newStr = '';
$.each(newValue, function (id, el) {
newStr += el.name() + '|';
});
newStr = newStr.substring(0, newStr.lastIndexOf("|"));
if (self.resolvedPropriety.peek() !== newStr) {
self.resolvedPropriety(newStr);
}
}
});
self.addChoice = function () {
//user added an option
self.choices.push({ name: ko.observable('new choice') });
};
self.removeChoice = function (data) {
//user removed an option
if (data) {
self.choices.remove(data);
}
};
...
}
This combination works, but not as I want to. It is a cyclic behavior and it triggers too many times. This is giving some overload on the user's actions because there are a lot of requests to the database.
What am I missing? Or is there a better way of doing it?
Quote from knockout computed observable documentation
... it doesn’t make sense to include cycles in your dependency chains.
The basic functionality I interpreted from the post:
Based on a field selection, display a list of properties/options
Have the ability to edit said property/option
Have the ability to add property/option
Have the ability to delete property/option
Have the ability to sort properties/options (its there, you have to click on the end/edge of the text field)
Have the ability to save changes
As such, I have provided a skeleton example of the functionality, except the last one, you described #JSfiddle The ability to apply the changes to the database can be addressed in several ways; None of which, unless you are willing to sacrifice the connection overhead, should include a computed or subscription on any changing data. By formatting the data (all of which I assumed could be collected in one service call) into a nice nested observable view model and passing the appropriate observables around, you can exclude the need for any ko.computed.
JS:
var viewModel = {
availableFields : ko.observableArray([
ko.observable({fieldId: 'Field1',
properties: ko.observableArray([{propertyName: "Property 1.1"}])}),
ko.observable({fieldId: 'Field2',
properties: ko.observableArray([{propertyName:"Property 2.1"},
{propertyName:"Property 2.2"}])})]),
selectedField: ko.observable(),
addProperty: function() {
var propertyCount = this.selectedField().properties().length;
this.selectedField().properties.push({propertyName: "Property " + propertyCount})
},
};
ko.applyBindings(viewModel);
$("#field-properties-list").sortable({
update: function (event, ui) {
//jquery sort doesnt affect underlying array so we have to do it manually
var children = ui.item.parent().children();
var propertiesOrderChanges = [];
for (var i = 0; i < children.length; ++i) {
var child = children[i];
var item = ko.dataFor(child);
propertiesOrderChanges.push(item)
}
viewModel.selectedField().properties(propertiesOrderChanges);
}
});
HTML:
<span>Select a field</span>
<select data-bind='foreach: availableFields, value: selectedField'>
<option data-bind='text: $data.fieldId, value: $data'></option>
</select>
<div style="padding: 10px">
<label data-bind='text: "Properties for " + selectedField().fieldId'></label>
<button data-bind='click: $root.addProperty'>Add</button>
<ul id='field-properties-list' data-bind='foreach: selectedField().properties'>
<li style = "list-style: none;">
<button data-bind="click: function() { $root.selectedField().properties.remove($data) }">Delete</button>
<input data-bind="value: $data.propertyName"></input>
</li>
</ul>
</div>

Categories

Resources