I have two layered MVC4 .NET Application with DAL and Web layers.
I am having problems when trying to bind data with Ajax returned data.
On Html, i am trying to get SubcriteriaList members and create the table for each member while filling their values.
HTML:
<h2 style="text-align:center">Criteria Info</h2>
<table align="center">
<tr>
<th colspan="3">Subcriteria Info</th>
</tr>
<tr>
<td>
<table class="table-condensed">
<tbody data-bind="foreach:SubcriteriaList">
<tr>
<td>
<table class="table-condensed">
<tr>
<th colspan="5" width="100%;">
<select style="width:100%;"></select>
</th>
<td>
<a class="btn btn-small btn-danger" href="#" style="margin-bottom:10px">X</a>
</td>
</tr>
<tr>
<td>
<label style="padding-top:5px;">Code</label>
</td>
<td>
<input class="input-large" placeholder="Code" data-bind="value:Code" />
</td>
<td>
<label style="padding-top:5px;">Weight</label>
</td>
<td>
<input class="input-large" placeholder="Weight" data-bind="value:Weight" />
</td>
</tr>
<tr>
<td>
<label style="padding-top:5px;">Name</label>
</td>
<th colspan="4" width="100%;">
<input style="width:100%;" class="input-large" placeholder="Name" data-bind="value:Name" />
</th>
</tr>
<tr>
<td>
<label style="padding-top:5px;">Goal</label>
</td>
<td>
<input class="input-large" placeholder="Goal" data-bind="value:Goal" />
</td>
<td>
<label style="padding-top:5px;">Achieved</label>
</td>
<td>
<input class="input-large" placeholder="Achieved" data-bind="value:Achieved" />
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr style="text-align:right">
<td>
<p>
<button class="btn btn-small btn-primary">Add Criteria</button>
</p>
</td>
</tr>
</table>
Knockout JS ViewModel in different JavaScript file.
JavaScript File:
function SubCriteriaViewModel() {
var self = this;
self.id = ko.observable("");
self.code = ko.observable("");
self.name = ko.observable("");
self.weight = ko.observable("");
self.goal = ko.observable("");
self.achieved = ko.observable("");
self.isActive = ko.observable("");
var Subcriteria = {
Id: self.id,
Code: self.code,
Name: self.name,
Weight: self.weight,
Goal: self.goal,
Achieved: self.achieved,
IsActive: self.isActive
};
self.Subcriteria = ko.observable();
self.SubcriteriaList = ko.observableArray();
// Initializing the view-model
$.ajax({
url: "/Subcriteria/GetAllSubcriteria",
cache: false,
type: 'GET',
contentType: 'application/json; charset=utf-8',
data: {},
success: function (data) {
alert(data);
//Probably Problem is here
self.SubcriteriaList(data); //Putting the response in ObservableArray
alert(SubcriteriaList);
alert(Subcriteria);
}
});
}
var viewModel = new SubCriteriaViewModel();
ko.applyBindings(viewModel);
When alert(data) hits i can see; [object Object],[object Object],[object Object]
return succesfuly however i can not add this JsonResult to SubCriteriaList array.
Thus (i think) i am getting
*Uncaught ReferenceError: Unable to process binding "value: function(){return Code }" Message: Code is not defined*
error.
How can i fill this SubcriteriaList array with this Ajax usage?
Thanks in advance.
Your data binding is Code, but your observable is code. Variable names are case sensitive. You'll need to fix all of them that do not match, as once you fix this one, the others will fail.
You've got some other issues though. You're not actually mapping the response to your view model. You should probably have a parent and child viewModel. The child would have the properties, and would be the map for the ajax response. The child would maintain the list, do the ajax request, etc.
function SubCriteriaViewModel(data) {
var self = this;
self.id = ko.observable(data.id);
self.code = ko.observable(data.code);
self.name = ko.observable(data.name);
self.weight = ko.observable(data.weight);
self.goal = ko.observable(data.goal);
self.achieved = ko.observable(data.achieved);
self.isActive = ko.observable(data.isActive);
}
function ViewModel() {
var self = this;
self.SubcriteriaList = ko.observableArray();
// Initializing the view-model
$.ajax({
url: "/Subcriteria/GetAllSubcriteria",
cache: false,
type: 'GET',
contentType: 'application/json; charset=utf-8',
data: {},
success: function (data) {
var subcriteriaList = data.map(function (item) { return new SubCriteriaViewModel(item); });
self.SubcriteriaList(subcriteriaList); //Putting the response in ObservableArray
}
});
}
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
Then, remember to fix all of the casing issues. Here's just one:
<input class="input-large" placeholder="Code" data-bind="value:Code" />
should be
<input class="input-large" placeholder="Code" data-bind="value:code" />
Related
Knockout observable value not updating on bootstrap Modal. In debug I can see that the value have been successfully added to observable. It just not updating/appearing on the modal DOM.
self.getSdsById = function (val) {
var ID = val.ID;
var operation = event.currentTarget.dataset['operation'];
$.ajax({
url: baseUrl + '/SdsView/getSdsByID',
cache: false,
type: 'GET',
contentType: 'application/json; charset=utf-8',
data: { 'ID': ID },
success: function (data) {
self.CopySdsDetail().RegistrationNo(data.Sds.RegistrationNo);
self.CopySdsDetail().MSDSIssueDate(data.Sds.MSDSIssueDate);
self.CopySdsDetail().Place(data.Sds.Place);
self.CopySdsDetail().Dept(data.Sds.Dept);
self.CopySdsDetail().Div(data.Sds.Div);
if (operation == 'COPY') {
$('#copyModalTitle').text('Copy & Add SDS to other Division');
$('#copyModal').modal();
}
}
}).fail(
function (xhr, textStatus, err) {
swal("Error", err, "error");
});
}
Modal :
<div class="modal fade" id="copyModal">
<div class="table-responsive" data-bind="with: CopySdsDetail">
<table class="table table-bordered table-sm">
<tr>
<td>Division: </td>
<td>Department: </td>
<td>Place: </td>
</tr>
<tr>
<td><input type="text" data-bind="value: Div"/></td>
<td><input type="text" data-bind="value: Dept"/></td>
<td><input type="text" data-bind="value: Place"/></td>
</tr>
<tr>
<td data-bind="text: RegistrationNo" colspan="2"></td>
<td data-bind="text: MSDSIssueDate"></td>
</tr>
</table>
</div>
</div>
Ko observable value in debug:
Any idea why?
in the short time I had to look at this, it generally looks ok, I got a very basic sample working with what you have provided. maybe it will help
var data = {
Dept: "Dicing",
Div: "DCG",
ID: 0,
MSDSIssueDate: "/Date(1602172800000)/",
Place: "Etching Process",
RegistrationNo: "DS-033"
}
function ViewModel() {
var self = this;
self.CopySdsDetail = ko.observable(new ModalViewModel());
self.clearData = function(){
self.CopySdsDetail(new ModalViewModel());
}
self.getData = function() {
self.CopySdsDetail(new ModalViewModel(data));
// self.CopySdsDetail().RegistrationNo(data.RegistrationNo);
// self.CopySdsDetail().MSDSIssueDate(data.MSDSIssueDate);
// self.CopySdsDetail().Place(data.Place);
// self.CopySdsDetail().Dept(data.Dept);
// self.CopySdsDetail().Div(data.Div);
}
}
function ModalViewModel(data){
var self = this;
data = data || {};
self.RegistrationNo = ko.observable(data.RegistrationNo || "");
self.Dept = ko.observable(data.Dept || "");
self.Div = ko.observable(data.Div || "");
self.ID = ko.observable(data.ID || "");
self.MSDSIssueDate = ko.observable(data.MSDSIssueDate || "");
self.Place = ko.observable(data.Place || "");
}
var vm = new ViewModel();
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="modal fade" id="copyModal">
<div class="table-responsive" data-bind="with: CopySdsDetail">
<table class="table table-bordered table-sm">
<tr>
<td>Division: </td>
<td>Department: </td>
<td>Place: </td>
</tr>
<tr>
<td><input type="text" data-bind="value: Div" /></td>
<td><input type="text" data-bind="value: Dept" /></td>
<td><input type="text" data-bind="value: Place" /></td>
</tr>
<tr>
<td data-bind="text: RegistrationNo" colspan="2"></td>
<td data-bind="text: MSDSIssueDate"></td>
</tr>
</table>
</div>
</div>
<button data-bind="click: getData">Get Data</button>
<button data-bind="click: clearData">Clear Data</button>
I'm trying to get the Knockout foreach binding working in my code. I've done it many times before, but in this particular case I can't seem to get it right. I must be doing something wrong here, but I can't see it. I created a jsfiddle here: https://jsfiddle.net/9Lc144jv/
JavaScript:
var ReportModel = function (parent) {
var self = this;
self.parent = parent;
self.filters = ko.observableArray([]);
self.addFilter = function () {
self.filters.push({ logicOperator: 0, columnName: null, comparisonOperator: 0, value: null });
};
self.removeFilter = function () {
self.filters.remove(this);
};
};
var ViewModel = function () {
var self = this;
self.reportModel = false;
self.init = function () {
self.reportModel = new ReportModel(self);
};
};
var viewModel;
$(document).ready(function () {
viewModel = new ViewModel();
ko.applyBindings(viewModel);
viewModel.init();
});
HTML:
<body>
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th>Column</th>
<th>Operator</th>
<th>Value</th>
<th></th>
</tr>
</thead>
<tbody>
<!-- ko foreach: reportModel.filters -->
<tr>
<td><input type="text" class="form-control" data_bind="value: logicOperator" /></td>
<td><input type="text" class="form-control" data_bind="value: columnName" /></td>
<td><input type="text" class="form-control" data_bind="value: comparisonOperator" /></td>
<td><input type="text" class="form-control" data_bind="value: value" /></td>
<td>
<button type="button" class="btn btn-danger" data-bind="click: $parent.removeFilter">
Remove
</button>
</td>
</tr>
<!-- /ko -->
</tbody>
<tfoot>
<tr>
<td colspan="5" style="text-align: right;">
<button type="button" class="btn btn-primary" data-bind="click: reportModel.addFilter">
Add
</button>
</td>
</tr>
</tfoot>
</table>
</body>
UPDATE
A few notes:
I was using the wrong attribute: "data_bind" instead of "data-bind". I corrected it now and the updated code is here: https://jsfiddle.net/9Lc144jv/2/
It's still not working even though I made that fix
When I copied and pasted it to a plain .html file and ran it, I could see something interesting in Firebug:
As you can see, running the following script in the command window shows there is nothing in the collection before clicking Add, and then something afterwards:
JSON.stringify(viewModel.reportModel.filters());
So the new object is in the observable array, but it's not binding to the table in the foreach block. But, why? From what I can see, everything looks fine.. but maybe I need a fresh pair of eyes on this. Someone please show me what I am doing wrong here...
You are setting reportModel after the bindings plus I had to add the function call to the buttons() .
slight changes:
var ReportModel = function (parent) {
var self = this;
self.parent = parent;
self.filters = ko.observableArray([]);
self.addFilter = function () {
self.filters.push({ logicOperator: 0, columnName: null, comparisonOperator: 0, value: null });
};
self.removeFilter = function () {
self.filters.remove(this);
};
};
var ViewModel = function () {
var self = this;
self.reportModel = new ReportModel(self);
self.init = function () {
console.info(self);
//self.reportModel = new ReportModel(self);
};
};
var viewModel;
$(document).ready(function () {
viewModel = new ViewModel();
ko.applyBindings(viewModel);
viewModel.init();
});
HTML
<body>
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th>Column</th>
<th>Operator</th>
<th>Value</th>
<th></th>
</tr>
</thead>
<tbody>
<!-- ko foreach: reportModel.filters -->
<tr>
<td><input type="text" class="form-control" data_bind="value: logicOperator" /></td>
<td><input type="text" class="form-control" data_bind="value: columnName" /></td>
<td><input type="text" class="form-control" data_bind="value: comparisonOperator" /></td>
<td><input type="text" class="form-control" data_bind="value: value" /></td>
<td>
<button type="button" class="btn btn-danger" data-bind="click: $parent.removeFilter()">
Remove
</button>
</td>
</tr>
<!-- /ko -->
</tbody>
<tfoot>
<tr>
<td colspan="5" style="text-align: right;">
<button type="button" class="btn btn-primary" data-bind="click: reportModel.addFilter()">
Add
</button>
</td>
</tr>
</tfoot>
</table>
</body>
https://jsfiddle.net/vw2kngqq/
I am getting the following error while selecting data using angular.js.
angularjs.js:107 TypeError: Cannot set property '0' of undefined
at n.$scope.removeBorder (adminCustomerNewController.js:55)
at fn (eval at <anonymous> (angularjs.js:212), <anonymous>:4:617)
at n.$eval (angularjs.js:132)
at angularjs.js:251
at angularjs.js:263
at m (angularjs.js:7)
at $$writeModelToScope (angularjs.js:263)
at angularjs.js:263
at g (angularjs.js:261)
at f (angularjs.js:261)
My code is given below.
<tr ng-repeat="d in days">
<td>{{d.day_name}}</td>
<td>
<table>
<tbody>
<tr ng-repeat="answer in d.answers">
<td>
<select class="form-control" id="answer_{{$index}}_{{$parent.$index}}_category" name="answer_{{$index}}_category" ng-model="answer.catagory" ng-options="cat.name for cat in listOfCatagory track by cat.value" ng-change="removeBorder($index,answer.catagory.value,$parent.$index);">
<option value="">Select Category</option>
</select>
</td>
</tr>
</tbody>
</table>
</td>
<td>
<table>
<tbody>
<tr ng-repeat="answer in d.answers">
<td>
<select class="form-control" id="answer_{{$index}}_{{$parent.$index}}_subcategory" name="answer_{{$index}}_subcategory" ng-model="answer.subcatagory" ng-options="sub.name for sub in listOfSubCatagory[$index][$parent.index] track by sub.value">
<option value="">Select Subcategory</option>
</select>
</td>
</tr>
</tbody>
</table>
</td>
<td>
<table>
<tbody>
<tr ng-repeat="answer in d.answers">
<td>
<input type="text" id="answer_{{$index}}_{{$parent.$index}}_comment" name="answer_{{$index}}_comment" placeholder="Add Comment" ng-model="answers.comment">
</td>
</tr>
</tbody>
</table>
</td>
<td>
<table>
<tbody>
<tr ng-repeat="answer in d.answers">
<td>
<input type="submit" name="plus" id="plus" value="+" style="width:20px; text-align:center;" ng-click="addNewRow(d.answers, true)">
<div ng-show="$first && !$last">
<input type="submit" name="minus" id="minus" value="-" style="width:20px; text-align:center;" ng-click="removeRow(d.answers, $last)">
</div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
My controller side code is given below.
$scope.days=[];
$http({
method:'GET',
url:"php/customerInfo.php?action=day",
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function successCallback(response){
//console.log('day',response.data);
angular.forEach(response.data, function(obj) {
obj.answers = [];
$scope.addNewRow(obj.answers);
$scope.days.push(obj);
});
},function errorCallback(response) {
})
}
$scope.addNewRow = function(answers, hasDelete) {
answers.push({
category: null,
subcategory: null,
comment: null,
hasDelete: hasDelete ? hasDelete : false
});
};
$scope.removeRow = function(answers, $index){
answers.splice($index, 1);
};
$scope.listOfSubCatagory = [];
// $scope.listOfSubCatagory = [[]]
$scope.removeBorder=function(index,catvalue,parent){
console.log('ind',index,catvalue,parent);
//$scope['listOfSubCatagory'+parent][index]=[];
//$scope.listOfSubCatagory[index][parent]=[[]];
$scope.listOfSubCatagory[index] = []
$scope.listOfSubCatagory[index][parent] = []
var catdata=$.param({'action':'subcat','cat_id':catvalue});
$http({
method:'POST',
url:"php/customerInfo.php",
data:catdata,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function successCallback(response){
angular.forEach(response.data,function(obj){
var data={'name':obj.subcat_name,'value':obj.subcat_id};
$scope.listOfSubCatagory[index][parent].push(data);
})
},function errorCallback(response) {
})
}
I am getting error in this $scope.listOfSubCatagory[index][parent]=[];.
The error means parent is 0 and $scope.listOfSubCatagory[index][0] is undefined.
You need to initialize listOfSubCatagory as a 2d array:
$scope.listOfSubCatagory = [[]];
instead of
$scope.listOfSubCatagory = [];
Also:
You override $scope.listOfSubCatagory[index][parent].push(data);
On every iteration of your for each loop.
Where do you initialize the full length of the 2d array? if parent or index was not 0
$scope.listOfSubCatagory[index][parent] would be undefined..
In your current script $scope.listOfSubCatagory[index] is undefined as you not initilizing array at this index.
Also need to initlize it at parent index as well
$scope.listOfSubCatagory[index][parent]=[]; // wrong one
The way you creating an array,is creating an sparse array.
$scope.listOfSubCatagory = [];
$scope.listOfSubCatagory[index] = []
$scope.listOfSubCatagory[index][parent] = []
EDIT 1:
instead of
$scope.listOfSubCatagory[index][parent].push(data);
use this
$scope.listOfSubCatagory[index][parent].push($.extend( {}, data));
I have 2 ng-app block on same page. One for listing items, and the other one is for insert. I want to call listing function after I have finished insert.
I have tried something but I couldn't succeeded in.
I found some documents about calling functions on same ng-app but i need to call a function from another ng-app.
Here is my code, please help.
<body>
<div id="UserControllerDiv" ng-app="UserTableApp" ng-controller="UsersController" class="form-group">
<br /><br />
<p>Search : <input type="text" ng-model="search" /></p>
<table class="table table-hover">
<thead>
<tr>
<th>First Name</th>
<th>Middle Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="u in users | filter:search">
<td>{{ u.FirstName }} </td>
<td>{{ u.MiddleName }} </td>
</tr>
</tbody>
</table>
</div>
<br />
<div id="UserFormDiv" ng-app="UserFormApp" ng-controller="UserFormCtr" class="form-group">
<table class="form-group">
<tr>
<td>First Name</td>
<td> : </td>
<td><input type="text" ng-model="FirstName" class="" /> </td>
</tr>
<tr>
<td>Middle Name</td>
<td> : </td>
<td><input type="text" ng-model="MiddleName" /> </td>
</tr>
<tr>
<td></td>
<td></td>
<td>
<button ng-click="submit()">Save</button>
</td>
</tr>
</table>
</div>
<script>
var userTableApp = angular.module("UserTableApp", []);
userTableApp.controller("UsersController",
function UsersController($scope, $http) {
$http.get("http://localhost:10160/UserService/Users")
.success(function (response) { $scope.users = response; });
});
var userFormApp = angular.module("UserFormApp", []);
userFormApp.controller("UserFormCtr",
function UserFormCtr($scope, $http) {
$scope.submit = function () {
var dataObj = {
FirstName: $scope.FirstName,
MiddleName: $scope.MiddleName
};
var res = $http.post("http://localhost:10160/UserService/AddUser", dataObj);
res.success(function (data, status, headers, config) {
/****** Here I want to call the UsersController function of UserTableApp ************/
/* I tried the code below but it did not work */
var scope = angular.element(document.getElementById("UserControllerDiv")).scope();
scope.$apply(function () { scope.UsersController($scope, $http); });
/*************************************************************************************/
});
res.error(function (data, status, headers, config) {
alert("failure message: " + JSON.stringify({ data: data }));
});
}
});
angular.bootstrap(document.getElementById("UserFormDiv"), ['UserFormApp']);
</script>
Thank you for all your help...
Here is how you call function from other controller app.
var scope = angular.element(document.getElementById("UserControllerDiv")).scope();
scope.yourfunctionName();
And the flow will go to the Function mentioned.
For assurance you can check presence of your method in scope object in console.
You were just about right.
I need a little help.
I have created a table that gets values from JSON response, but for this example lets create a hardcoded html table like following:
<table id="devtable">
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
</tr>
<tr>
<td>001</td>
<td>Jhon</td>
<td>Single</td>
</tr>
<tr>
<td>002</td>
<td>Mike</td>
<td>Married</td>
</tr>
<tr>
<td>003</td>
<td>Marrie</td>
<td>Complicated</td>
</tr>
</table>
ID : <input type="text" name="ID" data-bind="value: ID" disabled/>
<br>
Name : <input type="text" name="Name" data-bind="value: Name" disabled/>
<br>
Status : <input type="text" name="Status" data-bind="value: Status" disabled/>
<br>
<input type="button" value="Send" disabled/>
Requirement is: when I select a row of table, values of columns goes to the input boxes and enable button as well. As I am trying to learn Knockout.js by doing this exercise. I think I have to make a viewmodel like this:
var rowModel = function (id, name, status) {
this.ID = ko.observable(id);
this.Name = ko.observable(name);
this.Status = ko.observable(status);
}
Link of project is here: http://jsfiddle.net/qWmat/
Here's an example of how you could do it:
http://jsfiddle.net/qWmat/3/
function MyVM(data) {
var self = this;
self.items = ko.observableArray(data.map(function (i) {
return new rowModel(i.id, i.name, i.status);
}));
self.select = function(item) {
self.selected(item);
};
self.selected = ko.observable(self.items()[0]);
}
And you bind your textboxes to the properties in the selected property:
<input type="text" name="ID" data-bind="value: selected().ID" disabled/>
And you bind the click handler in your tr like so:
<tr data-bind="click: $parent.select">
Updated to include enable binding (http://jsfiddle.net/qWmat/8/). Add a property for whether or not to edit:
self.enableEdit = ko.observable(false);
Then update your select function to turn it to true:
self.select = function(item) {
self.selected(item);
self.enableEdit(true);
};
If / when you save or cancel you could the set it back to false if you want.
Update your bindings on the input boxes:
<input type="text" name="Status" data-bind="value: selected().Status, enable: enableEdit" />
I've created a demo for you, but to know how it works, you should investigate knockout documentation.
ViewModel:
<table id="devtable">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody data-bind="foreach: Items" >
<tr data-bind='click: $parent.setEditItem'>
<td data-bind="text: ID"></td>
<td data-bind="text: Name"></td>
<td data-bind="text: Status"></td>
</tr>
</tbody>
</table>
<!-- ko with: SelectedItem -->
ID :
<input type="text" name="ID" data-bind="value: ID, attr: {disabled: !$parent.IsEditMode()}" />
<br>Name :
<input type="text" name="Name" data-bind="value: Name, attr: {disabled: !$parent.IsEditMode()}"/>
<br>Status :
<input type="text" name="Status" data-bind="value: Status, attr: {disabled: !$parent.IsEditMode()}"/>
<br>
<input type="button" value="Send" data-bind="attr: {disabled: !$parent.IsEditMode()}"/>
<!-- /ko -->
Html:
function ItemModel(id, name, status) {
var self = this;
self.ID = ko.observable(id);
self.Name = ko.observable(name);
self.Status = ko.observable(status);
}
function ViewModel() {
var self = this;
self.Items = ko.observableArray([
new ItemModel('001', 'Jhon', 'Single'),
new ItemModel('002', 'Mike', 'Married'),
new ItemModel('003', 'Marrie', 'Complicated')
]);
self.SelectedItem = ko.observable(new ItemModel());
self.IsEditMode = ko.observable();
self.setEditItem = function(item) {
self.SelectedItem(item);
self.IsEditMode(true);
}
}
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
Demo