I have a page that displays a list of file templates built using the following method.
var loadCustomTemplate = function () {
loadBaseTemplate();
var res = 0;
for (i = 0; i < self.GetSeam().length; i++) {
var a = self.count() + 1;
self.count(a);
res = self.GetSeam()[i].FileFormat.split("_");
if (res.length == 4) {
var ap = res[3].split('.');
self.append(ap[0]);
} else {
self.append("");
}
var obj = {
Code: ko.observable(self.code()),
Number: ko.observable(self.number()),
SeamReportPath: ko.observable(self.reportPath()),
FileFormat: ko.observable(self.append()),
SequenceNumber: ko.observable(a)
}
self.CustomTemplate.push(obj);
}
self.count(0);
};
The user is then allowed to edit the fields as needed. They can also add records or remove them as needed. The method to add a record is as follows.
self.addTemplate = function () {
var count = self.CustomTemplate().length + 1;
var obj = {
Code: ko.observable(self.code()),
Number: ko.observable(self.number()),
SeamReportPath: ko.observable(self.reportPath()),
FileFormat: ko.observable(""),
SequenceNumber: ko.observable(count)
}
self.CustomTemplate.push(obj)
};
Once those updates are made they can save the updated CustomTemplate. This uses ajax that is not important to this question. The save method calls a validation method that is supposed to check to make sure there are no duplicate FileFormat fields in the object array. This is what I have, but it is failing.
var validateTemplates = function() {
for (i = 0; i < self.CustomTemplate().length; i++) {
var checkVal = self.CustomTemplate()[i].FileFormat;
var checkSeq = self.CustomTemplate()[i].SequenceNumber;
for (j = 0; j < self.CustomTemplate().length; j++) {
if (checkSeq !== self.CustomTemplate()[j].SequenceNumber ){
if (checkVal+"" === self.CustomTemplate()[j].FileFormat) {
if (checkSeq == self.CustomTemplate()[j].SequenceNumber ){
return false;
}
}
}
}
return true;
};
The problem is that when checking self.CustomTemplate()[i].FileFormat and self.CustomTemplate()[i].SequenceNumber it isn't reflecting the data displaying on the page or the data being sent to the controller (MVC 4). If I put either of those in an alert it is showing a function. How do I access the data in those specific fields for comparison?
Thanks in advance.
If I put either of those in an alert it is showing a function.
That's because you're doing this kind of thing:
var checkVal = self.CustomTemplate()[i].FileFormat;
FileFormat is the result of ko.observable(...), which returns a function, so checkVal in fact does contain a function.
The solution is for all those cases to do this:
var checkVal = self.CustomTemplate()[i].FileFormat(); // Parentheses at the end!
The parentheses execute the observable function, and if you do so without parameters you "get" the value of that observable. (If you would pass in a value, it would "set" the observable to that value.)
Related
I am trying to dynamically grab ZIPcodes and validate them when the length is 5.
I used querySelectorAll to grab the Zipcode fields on the page, as well as a few other fields I will use after validating.
I iterate over the nodelist and pass it to another function, where the eventlistener kicks off if the value is the correct length.
function GetZipCodeDetails() {
var zipId = document.querySelectorAll("[id*='ZipCode']");
var countyId = document.querySelectorAll("[id*='CountyId']");
var stateId = document.querySelectorAll("[id*='StateId']");
var phoneId = document.querySelectorAll("[id*='PhoneNumber']");
for (var i = 0; i < zipId.length; i++) {
if (zipId[i].length = 5)
AssortedZipCodeFunctions(zipId[i], countyId[i], stateId[i], phoneId[i]);
}
}
function AssortedZipCodeFunctions(zipId, countyId, stateId, phoneId) {
//Runs auto-county/state function only when zipcode field is completed
document.addEventListener("keyup", (e) => {
if (zipId.value.length == 5) {
GetCountyAndStateFromIds(zipId, countyId, stateId, phoneId);
} });
}
The code works perfectly for me as it is listed above; I am just trying to move the second function into the first function, but I can't figure out how. I am just stuck on how come I can't do the following:
function GetZipCodeDetails() {
var zipId = document.querySelectorAll("[id*='ZipCode']");
var countyId = document.querySelectorAll("[id*='CountyId']");
var stateId = document.querySelectorAll("[id*='StateId']");
var phoneId = document.querySelectorAll("[id*='PhoneNumber']");
for (var i = 0; i < zipId.length; i++) {
document.addEventListener("keyup", (e) => {
if (zipId[i].value.length == 5) {
GetCountyAndStateFromIds(zipId[i], countyId[i], stateId[i], phoneId[i]);
}
});
}
}
The above gives: "TypeError: Cannot read property 'value' of undefined
at HTMLDocument."
I have figured out that the for loop is calling the second function, instead of waiting until the Zipcode value is 5... so all that happened is I passed it to another function? Or maybe I am stuck on how to get the length of the value of a node item? Please help.
In your event listener you are adding it to the document instead of each element separately
for (var i = 0; i < zipId.length; i++) {
zipId[I].addEventListener("keyup", (e) => {
if (zipId[i].value.length == 5) {
GetCountyAndStateFromIds(zipId[i], countyId[i], stateId[i], phoneId[i]);
}
});
}
I'm trying to get either options or, ideally, dynamicTable passed from initializeTable to the applyTableFilters function and I'm having problems getting the expected values. I'm using List.js to make a table dynamic and I need to pass or recreate the dynamicTable object so I can go ahead and use it to filter the table.
Here is the function that creates the List.js object from the HTML table:
function initializeTable(options) { // initializes table to be dynamic using List.js functions
var dynamicTable = new List("table-content", options);
dynamicTable.on("updated", function (list) { // writes a message to the user if no results are found
if (list.matchingItems.length == 0) {
document.getElementById("no-results").style.display = "block";
}
else {
document.getElementById("no-results").style.display = "none";
}
});
console.log(dynamicTable);
console.log(options);
console.log(arguments.length);
applyTableFilters.bind();
}
I've tried different methods to pass the variables to the function below. I tried .call, applyTableFilters(args), and .apply, but the problem is that I do not want the function to execute from inside here, only when the click event from the button goes off (not shown in these functions).
This is the function I want to pass the object to and proceed to make the filter functions using it:
function applyTableFilters(dynamicTable) {
var form = document.getElementById("filter-form");
//console.log(options);
//var dynamicTable = new List("table-content", options);
console.log(dynamicTable);
var filters = form.querySelectorAll('input[type="checkbox"]:checked');
dynamicTable.filter(function (item) {
console.log(item);
console.log(item._values);
if (item.values().id == 2) {
return true;
}
else {
return false;
}
//var filterStrings = [];
//console.log(filters);
//for (var i = 0; i < filters.length; i++) {
// var filterVal = filters[i].value;
// var filterString = "(" + item.values().column == filterVal + ")"; // filterVal.contains(item.values().column) ||
// filterStrings.push(filterString);
// console.log(filterVal);
// console.log(filterString);
//}
//console.log(filterStrings);
//var filterString = filterStrings.join(" && ");
//console.log(filterString);
//return filterString;
});
}
I've used:
applyTableFilters.bind(this, dynamicTable/options);
applyTableFilters.bind(null, dynamicTable/options);
applyTableFilters.bind(dynamicTable/options);
Switching between the two since I don't need both passed if one ends up working, etc. I always get a mouse event passed in and that's not even the right type of object I'm looking for. How can I get the right object passed? Also all the values in the first function are not empty and are populated as expected so it's not the original variables being undefined or null. Thanks in advance.
From your initializeTable function return a function that wraps the applyTableFilters function with the arguments you want.
Then assign the returned function to a var to be executed later.
function initializeTable(options) {
var dynamicTable = new List("table-content", options);
// other stuff
return function () {
applyTableFilters(dynamicTable)
}
}
// other stuff
var applyTableFiltersPrep = initializeTable(options)
// later, when you want to execute...
applyTableFiltersPrep()
JSFiddle example
I've searched the questions here and on Google and I find answers, but they don't seem to work in my code. I want to add a new PhotoIndex property to a JSON object in AngularJS. I have the code below which works (the photos appear), but doesn't have the PhotoIndex property:
$scope.GetImagesOnBar = function ()
{
var photos = $scope.$parent.Photos;
return photos;
};
When I try to add the PhotoIndex property to the JSON array object named photos, it breaks the code and the photos no longer appear. Can you tell me why?
$scope.GetImagesOnBar = function ()
{
var photos = $scope.$parent.Photos;
var index = 0;
for (var i = $scope.ImageFirst; i <= $scope.ImageLast; i++)
{
photos[i].PhotoIndex = index;
index = index + 1;
}
return photos;
};
OK, thanks for the comments. I checked the console as suggested and saw the error "$scope.$parent.Photos[i] is undefined". I don't know why, but I decide to try angular.foreach and that worked. See the solution below.
$scope.GetImagesOnBar = function ()
{
var index = 0;
angular.forEach($scope.$parent.Photos, function (photo)
{
photo.PhotoIndex = index;
index = index + 1;
});
return $scope.$parent.Photos;
};
I have some JSON:
[{
"cityid":101,
"city":"Alta"
},
{
"cityid":102,
"city":"Bluffdale"
},
{
"cityid":105,
"city":"Draper"
},
{
"cityid":107,
"city":"Holladay"
}]
I can successfully search this array, and get the "city" value, with this function:
function getLocality(cid){
var storedlist = localStorage.getItem("citylist");
var clist = JSON.parse(storedlist);
for(var i = 0; i < clist.length; i++)
{
if(clist[i].cityid == cid) {
return clist[i].city;
} else {
}
}
}
My Issue:
When I try to use another function to get the city, instead of getting the cityid, it does not work.
The function I am using to try and get the city id, is as follows:
function getCityid(cid){
var storedlist = localStorage.getItem("citylist");
var clist = JSON.parse(storedlist);
for(var i = 0; i < clist.length; i++)
{
if(clist[i].city == cid) {
return clist[i].cityid;
} else {
}
}
}
I am calling the function as so:
getCityid('Draper');
I just opened up the console and tried both of your functions and they both seem to work. Since you didn't provide any sample input/output of your issue, I'll recommend the following:
Verify that you're passing in correct parameters into each function. The getLocality() should take in a cityid and getCityid() should take in a city.
Refrain from using == in javascript as this operator performs type coercion wherein things which are disparate types are "forced" to be the same type in order to perform comparison. You should instead use the === operator which will not perform type coercion. If the two things being compared are different types, it will simply evaluate to false.
Try this.
function getCityid(city) {
var storedlist = localStorage.getItem("citylist");
var clist = JSON.parse(storedlist);
for(var i = 0; i < clist.length; i++){
if(clist[i].city === city) {
return clist[i].cityid;
}
}
}
I have a question similar to Bind text to property of child object and I am having difficulty properly creating the KO observable for a child object.
For example, I do an HTTP Get to return a JSON array of People, and the People array is inside a property called "payload". I can get the basic binding to work, and do a foreach on the payload property, displaying properties of each Person; however, what I need to do is add a "status" property to each Person, which is received from a different JSON, example
/api/people (firstname, lastname, DOB, etc.)
/api/people/1/status (bp, weight, height, etc)
I have tried binding to status.bp, and status().bp, but no luck.
The js example:
var TestModel = function (data) {
var len = data.payload.length;
for (var i = 0; i < len; i++) {
var elem = data.payload[i];
var statusdata = $.getJSON("http://localhost:1234/api/people/" + elem.id + "/status.json", function (statusdata) {
elem.status = statusdata;
data.payload[i] = elem;
});
}
ko.mapping.fromJS(data, {}, this);
};
var people;
var data = $.getJSON("http://localhost:1234/api/people.json", function (data) {
people= new TestModel(data);
ko.applyBindings(people);
});
2 important things I will need:
1) properly notify KO that "payload" is an array to key on ID property
2) make "status" an observable
Help!
[UPDATE] EDIT with working fix based on Dan's answer:
var TestModel = function(data) {
...
this.refresh = function () {
$.getJSON("http://localhost:1234/api/people", function (data) {
self.payload = ko.observableArray(); // this was the trick that did it.
var len = data.payload.length;
for (var i = 0; i < len; i++) {
var elem = data.payload[i];
$.getJSON("http://localhost:1234/api/people/" + elem.id + "/status", function (statusdata) {
// statusdata is a complex object
elem.status = ko.mapping.fromJS(statusdata);
self.payload.push(elem);
});
}
// apply the binding only once, because Refresh will be called with SetInterval
if (applyBinding) {
applyBinding = false;
ko.applyBindings(self);
}
}
I am still new to Knockout and improvements to the refresh function are most welcome. The mapping is still being reapplied each time.
You need to define an observable array and then push your data into it.
elem.status = ko.observableArray();
for (var i = 0; i < statusdata.length; i++) {
elem.status.push(statusdata[i]);
}
I can't tell what the full structure of the data is by the example. But if status is a complex object, you may what to give it its own model.
for (var i = 0; i < statusdata.length; i++) {
elem.status.push(new statusModel(statusdata[i]));
}