I cant seem to get the binding to work on my KnockoutJS app.
JSFIDDLE -> http://jsfiddle.net/maylortaylor/pfqnkj17/
Here is the format of my JSON (generated by using <pre data-bind="text: ko.toJSON($root.forms,null,2)"></pre>)
[
{
"formTitle": "formTitle",
"formDescription": "formDesc",
"fieldTemplates": [
{
"fieldId": "text1",
"title": "title",
"description": "description fieldTemplate",
"isReq": true
},
{
"fieldId": "text2",
"title": "ttitle22",
"description": "description fieldTemplate 2",
"isReq": false
}
]
}
]
And here is how i am trying to call it in the page
<div id="MiddleColumn">
<input data-bind="textInput: $root.formTitle" type="text" placeholder="Title" class="span8 hideOffFocus input-full large-type">
<input data-bind="textInput: formDescription" type="text" placeholder="Description" class="hideOffFocus input-full">
</div
neither of those bindings work.
I create the forms object here
var FormModel = function (forms) {
var self = this;
self.forms = ko.observableArray(ko.utils.arrayMap(forms, function (form) {
return {
formTitle: form.formTitle, formDescription: form.formDescription,
fieldTemplates: ko.observableArray(form.fieldTemplates) };
}));
};
ko.applyBindings(new FormModel(initialData));
i hope your are expecting something like this
Working fiddle here
Now if you change something in textboxes in preview you can see automatic updates i.e mapping does make things back to ko way .
View Model :
var mapping = {
'fieldTemplates': {
create: function (options) {
return new FormModel(options.data);
}
}
}
function FormModel(forms) {
var self = this;
self.forms = ko.observableArray();
ko.mapping.fromJS(forms, mapping, self);
// other stuff
}
View :
<div id="MiddleColumn">
<input data-bind="textInput: $root.formTitle" type="text" />
<input data-bind="textInput: $root.formDescription" type="text"/><br/>
<div data-bind="foreach:$root.fieldTemplates">
<span data-bind="text:fieldId"></span>
<span data-bind="text:title"></span>
<span data-bind="text:description"></span>
<span data-bind="text:isReq"></span>
<br/>
</div>
</div>
Related
Angular 1 app here.
I have this json file named data.json:
[
{
"rejectionType": "REJECTION_1",
"user": "ALL",
"selected": false
},
{
"rejectionType": "REJECTION_2",
"user": "MALE",
"selected": false
},
{
"rejectionType": "REJECTION_3",
"user": "FEMALE",
"selected": false
}
]
In controller I do the following:
$http.get('data.json').then(function(response) {
var rejectionData = response.data;
myctrl.currentRejections = _.filter(rejectionData, function(item, index) {
return _.contains(["ALL", "MALE"], item.user);
})
console.log("myCtrl.currentRejections:",myCtrl.currentRejections);
$("#modRejectionReason").modal("show");
});
The modal in the view looks like this:
<div id="modRejectionReason">
<div class="modal-body">
<p>
<div ng-repeat="allrejections in myctrl.currentRejections">
<p>
<input type="radio" name="selected" ng-model="allrejections.selected" />
{{allrejections.rejectionType}}
</p>
</div>
</p>
</div>
<div class="modal-footer">
<button type="button" ng-click="myctrl.func()">OK</button>
</div>
</div>
</div>
</div>
And then in the controller I have this:
var declineInvite = function () {
console.log("myctrl.currentRejections:",myctrl.currentRejections);
}
In the log I see that when the modal appears, then the variable myCtrl.currentRejections is printed. And it is an array with all the filtered elements.
For each element I see that the field selected is false.
When I then check a radio button and then click on the OK-button the function func is triggered.
Even here the same data is printed in the console. However, for those radio buttons that have been clicked in the json the value for the field selected is undefined.
What am I missing here?
You need to give the radio buttons a value to set when they are checked.
<input type="radio" name="selected" ng-model="allrejections.selected" ng-value="true"/>
The problem with doing this in your current code is that the selected:true will never be unset, so I suggest adding a new value on the controller called selectedRejection and using that as the model and setting the value to the actual rejection object. Doing this means you can get rid of the selected property on your JSON data too!
var myApp = angular.module('myApp', []).controller("MyCtrl", MyCtrl);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl() {
var MyCtrl = this;
MyCtrl.currentRejections = [{
"rejectionType": "REJECTION_1",
"user": "ALL",
"selected": false
},
{
"rejectionType": "REJECTION_2",
"user": "MALE",
"selected": false
}
]
MyCtrl.selectedRejection = null;
MyCtrl.submit = function() {
console.log(MyCtrl.selectedRejection)
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl as MyCtrl">
<div ng-repeat="allrejections in MyCtrl.currentRejections">
<p>
<input type="radio" name="selected" ng-model="MyCtrl.selectedRejection" ng-value="allrejections" /> {{allrejections.rejectionType}}
</p>
</div>
<button type="button" ng-click="MyCtrl.submit()">OK</button>
</div>
</body>
I have the following JSON complex object.
{"User":
{
"$id":"2",
"Id":0,
"FirstName":null,
"LastName":null,
"Email":null,
"EmailConfirmed":false,
"PasswordHash":null,
}
}
How to bind this object in knockout js. So far I have used somethind like this to bind property with input field.
<input required class="form-control" data-bind="value: User.FirstName" type="text" />
Functions bo build model in knockout.
function userModel() {
var self = this;
self.User = ko.observable();
}
function bindData(data) {
userInfo.User(data["User"]);
}
When I call submiting via JS.
var jsonData = ko.toJSON(userInfo);
Object jsonData show that property like FirstName is null, however in formular value has been written.
Object userInfo stores written values in formular, I have checked it.
Should it look like this?
function userModel() {
var self = this;
self.Password = ko.observable();
self.User = ko.observable();
}
function UserViewModel(user) {
this.FirstName = ko.observable(user.FirstName);
this.LastName = ko.observable(user.LastName);
// other properties
}
function bindData(data) {
userInfo.Password(data["Password"]);
userInfo.User(new UserViewModel(data["User"]));
}
$(document).ready(function () {
userInfo = new userModel();
createUser();
ko.applyBindings(userInfo);
});
For two way binding to work, you need to build the same hierarchy of observable values on the view model.
Alternatively, you could use the mapping plugin:
Since User is also a observable, you have to update your binding like so:
<input required class="form-control" data-bind="value: User().FirstName" type="text" />
Since User has a lot of properties, you could benefit from the with binding:
Here's a fiddle with both examples (with and without the parent binding)
var data = {
"User": {
"$id": "2",
"Id": 0,
"FirstName": "Joseph",
"LastName": "Campbell",
"Email": null,
"EmailConfirmed": false,
"PasswordHash": null,
}
}
function UserViewModel(user) {
this.FirstName = ko.observable(user.FirstName);
this.LastName = ko.observable(user.LastName);
// other properties
}
function bindData(data) {
userInfo.User(new UserViewModel(data["User"]));
}
function userModel() {
var self = this;
self.User = ko.observable();
}
var userInfo = new userModel();
bindData(data);
ko.applyBindings(userInfo);
input {
display: block;
margin: 5px 0;
}
input[readonly] {
border: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<form data-bind="with: User">
<input type="text" data-bind="value: FirstName" />
<input type="text" data-bind="value: LastName" />
</form>
Current values:
<input type="text" readonly data-bind="value: User().FirstName" />
<input type="text" readonly data-bind="value: User().LastName" />
I cannot get a nested foreach to work. I have the following code:
HTML - Snippet
<div data-bind='foreach: choice'>
<p data-bind='foreach: id'>
<input name="group1" type="radio" data-bind="attr: { id: $data }"/> <label data-bind="attr: { for: $data} "> <span data-bind=" text: $data"> </span>
</label>
</p>
</div>
Javascript - Snippet
var questionModel = {
question : ko.observable(),
id: ko.observableArray(),
choice: ko.observableArray()
}
function startTest() {
questionModel.question(questions[questionNo].question);
var m = [];
var i = [];
var e = 0;
while (e != 4) {
m.push(choices[questionNo][e].choice);
i.push(choices[questionNo][e].id);
e++;
}
questionModel.choice(m);
questionModel.id(i);
}
Essentially what I'm trying to accomplish is for each choice to be generated within a radio button and to have the IDs within the array be the id for the radio button and label. I've successfully displayed the choices on it's own. But when I added the data-bind='foreach: id' & data-bind='attr: { id: $data }', that's when things stopped working. I keep getting the error below:
ReferenceError: Unable to process binding "foreach: function (){return
id }" Message: id is not defined
Disclaimer: I've tested the data and everything for the arrays are fine.
I'm sorry for not using your code layout, I couldn't find where questions was declared or where the bindings were applied. I made an example here using a simple 3 item data model, QuestionModel, and a simple view model containing it.
The data model just contains the ID for the question, the question itself (title) and then the choices for that question. Loop through each question, and then each of the choices. You can expand on the choices for values or whatever if you'd like. Just make an array of objects.
[{
"AnswerText": "Blue",
"AnswerValue" : "#0000FF"
}]
function QuestionModel(data) {
var self = this;
self.Id = ko.observable(data.Id);
self.Title = ko.observable(data.Title);
self.Choices = ko.observableArray(data.Choices);
}
function ViewModel() {
var self = this;
self.Questions = ko.observableArray([
new QuestionModel({
"Id": 1,
"Title": "What color are rabbits?",
"Choices": ["Red", "Blue", "Green"]
}),
new QuestionModel({
"Id": 2,
"Title": "What color are dogs?",
"Choices": ["Silver ", "Golden", "Striped"]
}),
new QuestionModel({
"Id": 3,
"Title": "What color are cats?",
"Choices": ["white", "Black", "Orange"]
})
]);
}
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div data-bind="foreach: Questions">
<span data-bind="text: Title"> </span>
<div data-bind="foreach: Choices">
<input type="radio" data-bind="value: $data, attr : {name : $parent.Id}" />
<span data-bind="text: $data"></span>
<br>
</div>
</div>
Since id is not part of choice, you need to use $parent to reference it:
data-bind="foreach: $parent.id"
https://jsfiddle.net/mbest/08gk7h4v/
You should add an array of choices that look like {id: 1} to the model. Then you can loop over the choices and reference the id property like so:
var questionModel = {
question: ko.observable(),
choice: ko.observableArray()
}
function startTest() {
questionModel.question(questions[questionNo].question);
var m = [];
var e = 0;
while (e != 4) {
var choice = choices[questionNo][e];
m.push(choice);
e++;
}
questionModel.choice(m);
}
<div data-bind="foreach: choice">
<p>
<input name="group1" type="radio" data-bind="attr: { id: id }" />
<label data-bind="attr: { for: id }">
<span data-bind="text: id"> </span>
</label>
</p>
</div>
monkeyStuff does what i want, it updates the span content if i write in the input field.
But why doesn't it work with the voteStuff?
See it in Action: Fiddle
<body>
<div id="monkeyStuff">
<input type="text" data-bind="value:monkey" />
<span data-bind="text:monkey"></span>
</div>
<hr>
<div id="voteStuff">
<div data-bind="text: test"></div>
<ul data-bind="foreach: voters">
<li>
<input type="text" data-bind="value:name" />
<span data-bind="text:name"></span>
</li>
</ul>
</div>
<script>
var vm = {
monkey: ko.observable()
};
vm.monkey("Quak");
ko.applyBindings(vm, document.getElementById('monkeyStuff'));
var model = {
test: 'Test address text',
voters: ko.observableArray([
{ name: 'First Voter' },
{ name: 'Second Voter' }
])
};
ko.applyBindings(model, document.getElementById('voteStuff') );
</script>
</body>
EDIT: OK it works like this:
voters: ko.observableArray([
{ name: ko.observable('First Voter') },
{ name: ko.observable('Second Voter') }
])
But is there a way to do it automatic for each property in the voters array?
You need to make the name property of the elements in your voters ko.observableArray also observable, which would thus allow you to alter these properties with the bindings you have implemented:
voters: ko.observableArray([
{ name: ko.observable('First Voter') },
{ name: ko.observable('Second Voter') }
])
Working example: http://jsfiddle.net/he2zoa3d/2/
Here is my static info:
$scope.users = {
"Eriks": {
"name": "Kreg",
"surname": "Indo",
"email": "example#example.com",
"age" : "2",
"tel" : "+123123 22"
}
};
I am adding a new user:
$scope.add = function () {
$scope.users.push({name:$scope.newUser.name, surname:$scope.newUser.surname, email:$scope.newUser.email, age:$scope.newUser.age, tel:$scope.newUser.tel});
$scope.newUser = "";
};
It works when I have different type of $scope.users like this
$scope.users = [{"name":"Kreg"}...more...];
and here is my form
<small>Name: </small> <input type="text" ng-model="newUser.name"><br/>
<small>Surname: </small> <input type="text" ng-model="newUser.surname"><br/>
<small>E-mail: </small> <input type="text" ng-model="newUser.email"><br/>
<small>Age: </small> <input type="text" ng-model="newUser.age"><br/>
<small>Telephone: </small> <input type="text" ng-model="newUser.tel"><br/>
<button ng-click="add()">add new user</button>
So what should I change in my add() function?
You can't push to an object like {}. Just set the key instead, like $scope.users[key] = userData;
Your second example works because it's a proper array that supports push.