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
Related
I need to reflect some changes to controller B (inside some event) when I make change at controller A. For that I am using a service.
When I am changing service value from FirstCtrl, ng-change is not firing at SecondCtrl. Is there anything I have missed or need to change?
Please note that I am using angular 1.5.6. and don't want to use watch or even scope.
Below is my code.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
debugger
self.changeM = function() {
debugger
Data.FirstName = self.FirstName;
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.FirstName = Data;
self.changeM = function() {
alert(1);
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="FirstCtrl as c">
<input type="text" ng-model="c.FirstName" data-ng-change="c.changeM()">
<br>Input is : <strong>{{c.FirstName}}</strong>
<div ng-controller="SecondCtrl as c1">
Input should also be here: {{c1.FirstName}}
<input type="text" ng-model="c1.FirstName" data-ng-change="c1.changeM()">
</div>
</div>
<hr>
</div>
As you dont want to use $scope trying modifying the code in order to use $emit and $on feature in angular js to communicate between two controllers. You can refer this link.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
debugger
self.changeM = function() {
debugger
//Data.FirstName = self.FirstName;
Data.$on('emitData',function(event,args){
Data.FirstName=args.message
document.write(Data.FirstName)
})
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.FirstName = Data;
self.changeM = function() {
Data.$emit('emitData',{
message:Data.FirstName
})
};
}
]);
The only way then is to directly copy the reference of the data object within the controller. Note that you don't need ng-change to update the value then.
If you want something else, either wrap the FirstName in a sub object of Data and do the same i did :
Data = {foo:'FirstName'};
Or use $watch since it's the whole purpose of that function.
Here is a working code with copying the Data object in the controller.
var myApp = angular.module('myApp', []);
myApp.factory('Data', function() {
return {
FirstName: ''
};
});
myApp.controller('FirstCtrl', ['Data',
function(Data) {
var self = this;
self.Data=Data;
debugger
self.changeM = function() {
debugger
};
}
]);
myApp.controller('SecondCtrl', ['Data',
function(Data) {
var self = this;
self.Data = Data;
self.changeM = function() {
alert(1);
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="FirstCtrl as c">
<input type="text" ng-model="c.Data.FirstName" data-ng-change="c.changeM()">
<br>Input is : <strong>{{c.Data.FirstName}}</strong>
<div ng-controller="SecondCtrl as c1">
Input should also be here: {{c1.Data.FirstName}}
<input type="text" ng-model="c1.Data.FirstName" data-ng-change="c1.changeM()">
</div>
</div>
<hr>
</div>
The only way I know to solve the problem is using watch, unfortunately. (I am new to angular.)
From the ngChange document (https://docs.angularjs.org/api/ng/directive/ngChange):
The ngChange expression is only evaluated when a change in the input value causes a new value to be committed to the model.
It will not be evaluated:
if the value returned from the $parsers transformation pipeline has not changed
if the input has continued to be invalid since the model will stay null
**if the model is changed programmatically and not by a change to the input value**
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());
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);
});
});
I'm new to Knockout and I'm building an app that's effectively a large-scale calculator. So far I have two instances of knockout running on one page. One instance is working perfectly fine, however the other one is entirely broken and just won't seem to register at all?
Below is my Javascript, fetchYear is the function that works perfectly fine and fetchPopulation is the one that's completely broken. It doesn't seem to register "ageview" from the HTML at all and I can't figure out.
The error:
Uncaught ReferenceError: Unable to process binding "foreach: function
(){return ageView }" Message: ageView is not defined
Thanks in advance.
JS:
var index = {
fetchYear: function () {
Item = function(year){
var self = this;
self.year = ko.observable(year || '');
self.chosenYear = ko.observable('');
self.horizon = ko.computed(function(){
if(self.chosenYear() == '' || self.chosenYear().horizon == undefined)
return [];
return self.chosenYear().horizon;
});
};
YearViewModel = function(yeardata) {
var self = this;
self.yearSelect = yeardata;
self.yearView = ko.observableArray([ new Item() ]);
self.add = function(){
self.yearView.push(new Item("New"));
};
};
ko.applyBindings(new YearViewModel(yearData));
},
fetchPopulation: function () {
popItem = function(age){
var self = this;
self.age = ko.observable(age || '');
self.chosenAge = ko.observable('');
self.population = ko.computed(function(){
if(self.chosenAge() == '' || self.chosenAge().population == undefined)
return [];
return self.chosenAge().population;
});
};
PopulationViewModel = function(populationdata) {
var self = this;
self.ageSelect = populationdata;
self.ageView = ko.observableArray([ new popItem() ]);
self.add = function(){
self.ageView.push(new popItem("New"));
};
};
ko.applyBindings(new PopulationViewModel(populationData));
}
}
index.fetchYear();
index.fetchPopulation();
HTML:
<div class="row" data-bind="foreach: yearView">
<div class="grid_6">
<img src="assets/img/index/calendar.png" width="120" height="120" />
<select class="s-year input-setting" data-bind="options: $parent.yearSelect, optionsText: 'year', value: chosenYear"></select>
<label for="s-year">Start year for the model analysis</label>
</div>
<div class="grid_6">
<img src="assets/img/index/clock.png" width="120" height="120" />
<select class="s-horizon input-setting" data-bind="options: horizon, value: horizon"></select>
<label for="s-horizon">Analysis time horizon</label>
</div>
</div>
<div class="row" data-bind="foreach: ageView">
<div class="grid_6">
<img src="assets/img/index/calendar.png" width="120" height="120" />
<select class="s-year input-setting" data-bind="options: ageSelect, optionsText: 'age', value: chosenAge"></select>
<label for="s-agegroup">Age group of <br> target population</label>
</div>
<div class="grid_6">
<img src="assets/img/index/clock.png" width="120" height="120" />
<input class="s-population input-setting"></input>
<label for="s-population">Size of your patient <br> population <strong>National</strong> </label>
</div>
</div>
When you do this (in fetchYear):
ko.applyBindings(new YearViewModel(yearData));
You are binding the entire page with the YearViewModel view model. But the YearViewModel doesn't have a property called ageView so you get the error and knockout stops trying to bind anything else.
What you need to do is restrict your bindings to cover only part of the dom by passing the element you want to ko.applyBindings. For example:
<div class="row" id="yearVM" data-bind="foreach: yearView">
//....
<div class="row" id="popVM" data-bind="foreach: ageView">
And then:
ko.applyBindings(new YearViewModel(yearData), document.getElementById("yearVM"));
//...
ko.applyBindings(new PopulationViewModel(populationData), document.getElementById("popVM"));
Now your bindings are restricted just to the part of the DOM that actually displays stuff from that model.
Another alternative is to just have your two view models as part of a parent view model and then you can apply the binding to the entire page. This makes it easier if you need to mix parts from both VMs and they are not conveniently separated in distinct sections of your page. Something like:
var myParentVM = {
yearVM : index.fetchYear(), // note, make this return the VM instead of binding it
popVM : index.fetchPopulation(), // ditto
}
ko.applyBindings(myParentVM);
And then you'd declare your bindings like so:
<div class="row" data-bind="foreach: yearVM.yearView">
The main reason why this is not working is because you call ko.applyBindings() more than once on a page (that is not really forbidden but is a bad practice in my opinion).
If you need to call it twice, you must call it with a container for which region this bind is meant to.
Something like this:
ko.applyBindings(new YearViewModel(yearData), document.getElementById('YourYearViewElementId'));
The error you get is from the first binding, which tries to process the whole page and does not find the 'ageView' in its ViewModel.
Better would be if you build a single ViewModel for a single Page where you have sub-models for sections if needed.
Some pseudo code for such a scenario:
var Section1ViewModel = function() {
var self = this;
self.property1 = ko.observable();
self.myComputed = ko.computed(function () {
// do some fancy stuff
});
self.myFunc = function() {
// do some more fancy stuff
};
}
var Section2ViewModel = function() {
var self = this;
self.property1 = ko.observable();
self.myComputed = ko.computed(function () {
// do some fancy stuff
});
self.myFunc = function() {
// do some more fancy stuff
};
}
var PageViewModel = function() {
var self = this;
self.section1 = ko.observable(new Section1ViewModel());
self.section2 = ko.observable(new Section2ViewModel());
self.myGlobalFunc = function() {
// do some even more fancy stuff
}
}
ko.applyBindings(new PageViewModel());
Hope that helps.
Best regards,
Chris
I'm trying to bind a 1-many mapping using KnockoutJS, where 1 zip code can have many 'agents'. I have the following classes:
function CaseAssignmentZipCode(zipcode, agent) {
var self = this;
self.zipcode = ko.observable(zipcode);
self.agent = ko.observable(agent);
}
function Agent(id, name) {
var self = this;
self.id = id;
self.name = name;
}
function ZipcodeAgentsViewModel() {
var self = this;
self.caseAssignmentZipCodes = ko.observableArray([]);
self.agents = ko.observableArray([]);
jdata = $.parseJSON($('#Agents').val());
var mappedAgents = $.map(jdata, function (a) { return new Agent(a.Id, a.Name) });
self.agents(mappedAgents);
var dictAgents = {};
$.each(mappedAgents, function (index, element) {
dictAgents[element.id] = element;
});
var jdata = $.parseJSON($('#CaseAssignmentZipCodes').val());
var mappedZipcodeAgents = $.map(jdata, function (za) { return new CaseAssignmentZipCode(za.ZipCode, dictAgents[za.UserId], false) });
self.caseAssignmentZipCodes(mappedZipcodeAgents);
}
var vm = new ZipcodeAgentsViewModel()
ko.applyBindings(vm);
My bindings look like this:
<table>
<thead><tr><th>Zipcode Agents</th></tr></thead>
<tbody data-bind="foreach: caseAssignmentZipCodes">
<tr>
<td><input data-bind="value: zipcode"></td>
<td><select data-bind="options: $root.agents, value: agent, optionsText: 'name'"></select></td>
<td>Remove</td>
</tr>
</tbody>
</table>
Everything binds fine the first time, with the table and select fields appearing properly. However, nothing happens when I change the selected value on any of the select elements. I have bound other elements to them and these don't update, and I've tried using .subscribe() to listen for the update event, but this doesn't fire either.
I expect there's something wrong with the way I'm setting up/binding these relationships, but I can't figure it out to save my life.
Thanks!
I think you need to add
self.agents = ko.observableArray([]);
at the top of ZipcodeUsersViewModel