Knockout.js -Getting error - Uncaught ReferenceError: Unable to process binding "with: function" - javascript

I am working on a neighborhood map project and I am stuck! I am new to knockout.js. I am trying to use data-bind getting this error -
knockout-3.4.1.js:72 Uncaught ReferenceError: Unable to process binding "with: function (){return filteredItems }"
The snippet of HTML source -
section class="main">
<form class="search" method="post" action="index.html" >
<input type="text" data-bind="textInput: filter" placeholder="Click here/Type the name of the place">
<ul data-bind="with: filteredItems">
<li><span data-bind="text: title, click: $parent.showInfoWindow"></span></li>
</ul>
</form>
</section>
and this is my viewModel -
function viewModel(markers) {
var self = this;
self.filter = ko.observable(''); // this is for the search box, takes value in it and searches for it in the array
self.items = ko.observableArray(locations); // we have made the array of locations into a ko.observableArray
// attributed to - http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html , filtering through array
self.filteredItems = ko.computed(function() {
var filter = self.filter().toLowerCase();
if (!filter) {
return self.items();
} else {
return ko.utils.arrayFilter(self.items(), function(id) {
return stringStartsWith(id.name.toLowerCase(), self.filter);
});
}
});
var stringStartsWith = function (string, startsWith) {
string = string || "";
if (startsWith.length > string.length)
return false;
return string.substring(0, startsWith.length) === startsWith;
};
// populateInfoWindow(self.filteredItems,)
// this.showInfoWindow = function(place) { // this should show the infowindow if any place on the list is clicked
// google.maps.event.trigger(place.marker, 'click');
// };
}
Some lines are commented because I am still working on it. To see the whole project- https://github.com/Krishna-D-Sahoo/frontend-nanodegree-neighborhood-map

The with binding creates a new binding context with the provided element. The error is thrown because of a reference to title within the <span> element, but filteredItems does not have a title property.
If you want to display a <li> element for each element in the filteredItems array, you can use a foreach binding, like this:
<ul data-bind="foreach: filteredItems">
<li><span data-bind="text: title, click: $parent.showInfoWindow"></span></li>
</ul>

Related

Cannot read property 'push' of undefined - knockout observableArray

When I click this button, an object is created and added to the array which is reflected in UI. Next when I click this button again I get an error "Cannot read property 'push' of undefined". What could possibly be the issue? Since the context seems fine otherwise it wouldn't have called the appropriate function in Rule class.
html code
<div data-bind="with: formatterVM">
<div data-bind="with: currentRule">
<ul data-bind="foreach: conditions">
<li data-bind="html: x"></li>
<li data-bind="html: y"></li>
</ul>
<button data-bind="click: addCondition">Click</button>
</div>
</div>
JS/Knockout code
function ReportsVM() {
self = this
self.formatterVM = new FormatterVM()
}
function FormatterVM(){
self = this
self.rules = ko.observableArray()
self.currentRule = ko.observable(new Rule())
}
function Rule(){
self = this
self.conditions = ko.observableArray()
self.addCondition = function(){
self.conditions.push(new Condition(2,3))
}
}
function Condition(x,y){
self = this
self.x = ko.observable(x)
self.y = ko.observable(y)
}
// Activates knockout.js
ko.applyBindings(new ReportsVM());
Working JSFiddle
https://jsfiddle.net/etppe937/
Update
It was an issue with the scope of the self variable. We need to use var keyword in order to not let the variable have a global scope. JSFiddle has been updated.

Knockout JS failed to update observableArray

So I'm trying to add content to an observable array, but it doesn't update. The problem is not the first level content, but the sub array. It's a small comments section.
Basically I've this function to declare the comments
function comment(id, name, date, comment) {
var self = this;
self.id = id;
self.name = ko.observable(name);
self.date = ko.observable(date);
self.comment = ko.observable(comment);
self.subcomments = ko.observable([]);
}
I've a function to retrieve the object by the id field
function getCommentByID(id) {
var comment = ko.utils.arrayFirst(self.comments(), function (comment) {
return comment.id === id;
});
return comment;
}
This is where I display my comments
<ul style="padding-left: 0px;" data-bind="foreach: comments">
<li style="display: block;">
<span data-bind="text: name"></span>
<br>
<span data-bind="text: date"></span>
<br>
<span data-bind="text: comment"></span>
<div style="margin-left:40px;">
<ul data-bind="foreach: subcomments">
<li style="display: block;">
<span data-bind="text: name"></span>
<br>
<span data-bind="text: date"></span>
<br>
<span data-bind="text: comment"></span>
</li>
</ul>
<textarea class="comment" placeholder="comment..." data-bind="event: {keypress: $parent.onEnterSubComment}, attr: {'data-id': id }"></textarea>
</div>
</li>
</ul>
And onEnterSubComment is the problematic event form
self.onEnterSubComment = function (data, event) {
if (event.keyCode === 13) {
var id = event.target.getAttribute("data-id");
var obj = getCommentByID(parseInt(id));
var newSubComment = new comment(0, self.currentUser, new Date(), event.target.value);
obj.subcomments().push(newSubComment);
event.target.value = "";
}
return true;
};
It's interesting, because when I try the same operation during initialization(outside of any function) it works fine
var subcomment = new comment(self.commentID, "name1", new Date(), "subcomment goes in here");
self.comments.push(new comment(self.commentID, "name2", new Date(), "some comment goes here"));
obj = getCommentByID(self.commentID);
obj.subcomments().push(subcomment);
If anyone can help me with this, cause I'm kind of stuck :(
You need to make two changes:
1st, you have to declare an observable array:
self.subcomments = ko.observableArray([]);
2nd, you have to use the observable array methods, instead of the array methods. I.e. if you do so:
obj.subcomments().push(subcomment);
If subcomments were declared as array, you'd be using the .push method of Array. But, what you must do so that the observable array detect changes is to use the observableArray methods. I.e, do it like this:
obj.subcomments.push(subcomment);
Please, see this part of observableArray documentation: Manipulating an observableArray:
observableArray exposes a familiar set of functions for modifying the contents of the array and notifying listeners.
All of these functions are equivalent to running the native JavaScript array functions on the underlying array, and then notifying listeners about the change

Accessing li data on click from ng-repeat

How can I log/access object data from an ng-repeat? I would like to be able to access each specific firebase object when I click on its li element. I have been able to log the entire object, but I need to target just that specific li's data?
pomodoro_timer.controller('timer', ['$scope','$interval','$firebaseArray', function ($scope, $interval, $firebaseArray) {
var ref = new Firebase("https://pomodoro-timer7710.firebaseIO.com/");
$scope.working = $firebaseArray(ref)
$scope.addWork = function() {
if ($scope.newWork == null) {
WorkInputField.placeholder = "Please enter a job"
}
$scope.working.$add({
name: $scope.newWork,
startedAt: Date.now(),
completed: false
});
$scope.newWork = "";
}
$scope.showTime = function() {
ref.on("value", function(snapshot) {
console.log(snapshot.val());
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
}
HTML
<div id="workInfo">
<div id="enterWork">
<form ng-submit="addWork()">
<input id="WorkInputField" type="text" placeholder="Job to complete" ng-model="newWork"></input>
</form>
<h3>Your Work:</h3>
<ul>
<li class="workList" ng-repeat="work in working" ng-click="showTime()">
{{ work.name }}
<span class="removeWork">Done</span>
</li>
</ul>
</div>
</div>
I hope this is enough code, its all that pertains to the problem. When I click on each individual li I want to view its key/value pairs. Thanks, I'm trying to wrap my head around Angular and this one's been driving me nuts.
HTML:
<li class="workList" ng-repeat="work in working" ng-click="showTime(work)"> ...</li>
Controller:
$scope.showTime = function(work) { console.log(work); }
Just read the angular document of ng-repeat and you'll understand how it works.

Knockout: Can't get specific value from observableArray

I created array with data about some music albums in viewmodel. I can use this array in foreach loop (for example) but I can't display only one value from this array.
My viewmodel:
function CD(data) {
this.Author = ko.observable(data.Author);
this.Title = ko.observable(data.Title);
this.Price = ko.observable(data.Price);
this.Label = ko.observable(data.Label);
this.Id = ko.observable(data.Id);
}
function NewsListViewModel() {
var self = this;
self.newsList = ko.observableArray([]);
$.getJSON("/music/news", function (allData) {
var mapped = $.map(allData, function (item) { return new CD(item) });
self.newsList(mapped);
});
self.oneObject = ko.computed(function () {
return self.newsList()[0].Title;
});
}
$(document).ready(function () {
ko.applyBindings(new NewsListViewModel());
});
And how I used it in html:
<ul data-bind="foreach: newsList">
<li data-bind="text: Title"></li>
<li data-bind="text: Author"></li>
<li data-bind="text: Price"></li>
<li data-bind="text: Label"></li>
</ul>
This works perfectly. But if I trying additionally display one value:
<ul data-bind="foreach: newsList">
<li data-bind="text: Title"></li>
<li data-bind="text: Author"></li>
<li data-bind="text: Price"></li>
<li data-bind="text: Label"></li>
</ul>
...
<p data-bind="text: oneObject"</p>
it dosen't working, firebug thrown error:
Type error: self.newsList()[0].Title is undefined
Why I cannot get one specific value but I can display whole list?
PS. Code <p data-bind="text: newsList()[0].Title"</p> didn't works too.
You get the error because $.getJSON is asynchronous.
So when your computed is declared your items are not necessary ready in the newsList so there is no first element what you could access.
So you need to check in your computed that your newsList is not empty (anyway your server could also return an empty list so it is always good to have this check) and only then access the first element:
self.oneObject = ko.computed(function () {
if (self.newsList().length > 0)
return self.newsList()[0].Title();
return 'no title yet';
});
Note: you need also write .Title() to get its value because Title is an observable.
I can see a few problems with your code:
$.getJSON is asynchronous so self.newsList is empty when it's accessed by Knockout for the first time. Which in turn means that self.newsList[0] is undefined.
return self.newsList()[0].Title; in the computed observable doesn't return the value you want it to return. It should be return self.newsList()[0].Title();
You don't have to reinvent the wheel, there is the mapping plugin which you can use to map the data: http://knockoutjs.com/documentation/plugins-mapping.html

Durandal 2.0 - Update the value of an observable in the view

I'm new to Durandal, my question has probably a very simple issue.
I load a list into a dropdown and the current value on the link which display the dropdown,
And the value of the link which display the dropdown is not updated correctly when an other value is selected.
But actually, I can't set the value of the observable in the select function.
View Model
var self = this;
self.system = require('durandal/system');
IPsKeys: ko.observableArray([]),
ipKeys: ko.observable(""),
activate: function (context) {
var that = this;
that.IPsKeys([]);
that.ipKeys("");
return $.when(
service.getIPSbyClientId(context.clientId).then(function (json) {
$.each(json, function (Index, Value) {
var ClientLobUWYear = {
NameLob: Value.LineOfBusiness.Name,
NameUWYear: Value.UnderwritingYear
};
that.IPsKeys().push(ClientLobUWYear);
// HERE MY VALUE IS GOOD UPDATING AND THE BINDING WORK
if (Index=== 0) {
that.ipKeys(ClientLobUWYear);
}
});
})
).then(function () {
//do some other datacontext calls for stuff used directly and only in view1
});
},
select: function (item) {
this.ipKeys = {
IdClient: item.IdClient,
IdLob: item.IdLob,
NameLob: item.NameLob,
NameUWYear: item.NameUWYear
};
/** PROBLEMS HERE **/
/** Uncaught TypeError: undefined is not a function **/
this.ipKeys(ClientLobUWYear);
},
View
<a id="select_lob-UWYear" class="dropdown-toggle" data-toggle="dropdown" href="#">
<span class="controls_value" data-bind="text: ipKeys().NameLob">ALOB</span>
<span class="controls_value" data-bind="text: ipKeys().NameUWYear">AYEAR</span>
</a>
<ul id="dropdown_year" class="dropdown-menu" data-bind="foreach: IPsKeys().sort(sortByLobYear)">
<li>
<a href="#" data-bind="click: $parent.select">
<span class="controls_value" data-bind="text: NameLob">Cargo</span>
<span class="controls_value" data-bind="text: NameUWYear">2014</span>
</a>
</li>
</ul>
Thanks a lot
The way you update an observable is like this:
var someObservable = ko.observable(""); //setting to "";
someObservable("Something else"); //updating to "Something else"
Not like this (which you are doing above)
var someObservable = ko.observable(""); //setting to "";
someObservable = "Something else";
This is overwriting someObservable with a string of value "Something else" and so is no longer an observable which is why it will not update the ui.
[JS Fiddle showing how to set observables.]

Categories