Knockout.js returns undefined - javascript

I am trying to fetch some user data from ajax and return it in my html to update the UI.
For some reason I cant get it to work.
I have the following knockout:
function User(data) {
this.name = ko.observable(data.name);
this.email = ko.observable(data.email);
}
function UserViewModel() {
// Data
var self = this;
self.users = ko.observableArray([]);
self.newTaskText = ko.observable();
self.incompleteTasks = ko.computed(function() {
return ko.utils.arrayFilter(self.users(), function(user) { return !user.email() });
});
// Load initial state from server, convert it to Task instances, then populate self.tasks
$.getJSON("/admin/ajax-get-add-user-json.do", function(allData) {
var mappedTasks = $.map(allData, function(item) { return new User(item) });
self.users(mappedTasks);
console.log(allData);
console.log(allData.name);
});
}
ko.applyBindings(new UserViewModel());
Then I return it like this in the html:
<ul data-bind="foreach: users, visible: users().length > 0">
<li data-bind="text: email"></li>
<li data-bind="text: name"></li>
</ul>
But when I use the console.log for .name it returns undefined in my console.
And it doesnt print anything in the HTML either.
What am I doing wrong here?

You need to read the response property of all data:
function(allData) {
var data = allData.response;
var mappedTasks = $.map(data, function(item) { return new User(item) });
self.users(mappedTasks);
}

Related

Return a promise with a model and a service

I have an error with a promise.
I try to use angular-tree-dnd , but I have a problem with a promise.
from my controller :
project.getAll().then(function(results) {
var projects = results;
$scope.results = [];
angular.forEach(projects, function(result) {
$scope.results.push(project.build(result));
});
return $scope.results;
});
$scope.tree_data = $TreeDnDConvert.line2tree($scope.results, 'id', 'ParentId');
my model :
var Project = function(properties) {
// Model
this.description = null;
this.file = null;
this.name = null;
this.ParentId = null;
this.path = null;
angular.extend(this, properties);
};
Project.prototype.setModel = function(obj) {
angular.extend(this, obj);
};
Project.prototype.getAll = function() {
return ProjectService.getAll();
};
Project.prototype.build = function(data) {
var project = new Project(data);
return project;
};
my service (with $webSql) :
this.getAll = function(params) {
var projects = [];
return db.selectAll('projects').then(function(results) {
for (var i = 0; i < results.rows.length; i++) {
projects.push(results.rows.item(i));
}
return projects;
});
};
I have no error but my home is empty.
I tried this :
project.getAll().then(function(results) {
var projects = results;
$scope.results = [];
angular.forEach(projects, function(result) {
$scope.results.push(project.build(result));
});
return $scope.results;
}).then(function(array) {
$scope.tree_data = $TreeDnDConvert.line2tree(array, 'id', 'ParentId');
});
But I have this error :
angular.min.js:13550 TypeError: Cannot read property '__children__' of undefined
at Object._$initConvert.line2tree (ng-tree-dnd.js:1456)
I think that my array is empty
I suspect that this is the root of your problem:
"ParentId":"null"
Both of the items on your tree have a ParentId with the string value "null". This probably means that the dnd library is looking for a node with an ID of "null", finding nothing, and trying to access the __children__ property on that undefined value.
The solution: fix the data in your DB so that the parent IDs are actually null values and not the value "null".
Good evening)
Please set a breakpoint, or use debugger, alert, console.log with JSON.stringify(array), before your last operation:
$scope.tree_data = $TreeDnDConvert.line2tree(array, 'id', 'ParentId')
(last version with then and error).
I think you got correct array and trouble not in promises, but in line2tree syntax.

Cannot set property of "something" of undefined error while trying to create an array sorted by dates.

I posted a related question here on Stack Overflow and #Kato helped me out with an answer. I am attempting to implement his answer but getting an error of Cannot set property of "something" of undefined.
I made a plnkr to be as detailed as possible: http://plnkr.co/edit/M4zEjpZ4kqTKn1sHHMt6
//controller
angular.module('app').controller('DemoCtrl', function($scope, DatedList) {
$scope.world = 'world';
var listRef = new Firebase("https://talllly.firebaseio.com/");
$scope.weeks = DatedList(listRef);
$scope.addTask = function(){
listRef.push({
text: $scope.task.text,
week: 40,
day: 2
});
};
});
angular.module('app').service('DatedList', function($timeout) {
return function(pathToList) {
var list = {};
pathToList.on('child_added', function(snap) {
$timeout(function() { // force Angular to run $digest when changes occur
var data = snap.val();
console.log(data);
var week_number = data.week;
var week_day = data.day;
list[week_number][week_day] = data;
});
});
//todo: write similar processing for child_changed and child_removed
return list;
}
});
//html
<form ng-submit="addTask()">
<input placeholder="add task" ng-model="task.text">
</form>
<div ng-repeat="(week, days) in weeks">
<h1>{{week}}</h1>
<div ng-repeat="(day, items) in days">
<h2>{{day}}</h2>
<div ng-repeat="item in items">
{{item|json}}
</div>
</div>
</div>
You need to initialize list[week_number]:
pathToList.on('child_added', function(snap) {
$timeout(function() { // force Angular to run $digest when changes occur
var data = snap.val();
console.log(data);
var week_number = data.week;
var week_day = data.day;
list[week_number] = {}; // <- Add this line here
list[week_number][week_day] = data;
});
});
The reason you're getting that error is because you're trying to add a property to list[week_num] and it isn't an object.
You could do
list[week_number] = list[week_number] || {};
list[week_number][week_day] = data;
or
if(!angular.isObject(list[week_number]) {
list[week_number] = {};
}
list[week_number][week_day] = data;

How to get instance of nested view model- knockout

The view has a heading followed by section with has submenus. the design for the viewmodels is below:
SettingsViewModel = function (pName) {
var self = this;
self.productName = ko.observable(pName), //heading
self.sections = ko.observableArray([
{ checkboxID: ko.observable(), checkboxIDState: ko.observable(), sectionname: ko.observable(), sectionState: ko.observable() }
]), //submenus
self.Addsections = function (checkboxid, sIdState, sName, sState) {
this.sections.push({ checkboxID: checkboxid, checkboxIDState: sIdState, sectionname: sName, sectionState: sState });
}
};
function MainViewModel() {
var self = this;
self.products = ko.observableArray([]);
self.AddProducts= function (pname) {
self.products.push(new SettingsViewModel(pname));
}
};
$(document).ready(function () {
VM = new MainViewModel();
ko.applyBindings(VM, document.getElementById("divd"));
data= []; //some dummy data
CallMEnus(data);
});
function CallMEnus(data) {
var str = "";
$(data).each(function (index, products) {
VM.AddProducts(products.name);
$(products.section).each(function (index, section) {
var ChkboxId = "data";
var chkboxIdState = 'datt';
var chkboxIdState += " checked";
}
//how to call the products add section method?
VM.products()[index].Addsections(ChkboxId, chkboxIdState, section.name, section.state);
});
});
I need to call the AddSections method of the nested SettingsViewModel from MainViewModel instance. How to achieve this?
Thanks in advance.
Your problem is that parameter index from sections loop hides index from products loop. Just use another parameter name:
function CallMEnus(data) {
var str = "";
$(data).each(function (index, products) {
VM.AddProducts(products.name);
$(products.section).each(function(i, section) { // here
var id = "data";
var state = "checked";
VM.products()[index].Addsections(id, state, section.name, section.state);
});
});
};
Fiddle
I would use a EventAggregator to decouple viewmodels, I've written this lightweight EventAggregator
http://jsfiddle.net/wJtun/4/
Subscribe:
MyApp.eventAggregator.subscribe(MyApp.DeleteCustomerMessage, this.customerDeleted, this);
Publish:
MyApp.eventAggregator.publish(new MyApp.DeleteCustomerMessage(this));

knockoutjs, How do you use an observableArray to store the history of another observable?

I'm new to Knockoutjs. I have a very simple model with name, value and history variables. I've tried several approaches but can not get the history to update more then 1 iteration. Most approaches failed with scope access issues. As I said I'm new to knockeout. I've also included this Fiddle that illustrates the issues well.
this.tlmname = ko.observable();
this.tlmval = ko.observable();
this.history = ko.observableArray();
var telemItem = function (data) {
this.tlmname = ko.observable();
this.tlmval = ko.observable();
this.history = ko.observableArray();
this.update(data);
};
ko.utils.extend(telemItem.prototype, {
update: function(data) {
this.tlmname(data.tlmname|| "Cheese");
this.tlmval(data.tlmval || 0);
this.history.push(data.tlmval);
if (this.history().length > 50) this.history.shift();
}
});
var telemetryViewModel = function(telemVars) {
this.telemVars = ko.observableArray(ko.utils.arrayMap(telemVars, function(data) {
return new telemItem(data);
}));
function UpdateModel( modelView ) {
$.getJSON('/gtr/tests/ko/requestTelemetry.php',
function(data)
{
//modelView.telemVars(data.telemVars);
//modelView(data.telemVars);
modelView.update(data.telemVars);
modelView.telemVarsDirect( getDirectVM( data.telemVars ) );
}
)};

How to iterate a ko.ObervableArray

I have this structure:
MyApp.User = function()
{
var self = this;
self.ID = ko.obervable();
self.Name = ko.obervable();
self.LastName = ko.observable();
}
MyApp.UserHub = function()
{
self.users = ko.observableArray();
$.getJSON("url", function (data) {
var mappedUser = $.map(data.UsersFromJson, function (item) {
return new MyApp.User(item);
});
self.users(mappedUsers);
});
}
I have a observableArray, which I populated using a HTML Request and a JSON (That works just fine). The thing is that I want to be able to search in that observableArray a user, providing information that can be contained in the LastName or in the FirstName. Something like this:
self.searchedUsers = ko.observableArray();
for(var item in users)
{
if(item.FirstName.Contains(query) || item.LastName.Contains(query))
{
self.searchedUser.push(item);
}
}
Query is the input text value that I want to search. Can anyone help to iterate that ObservableArray?
Generally, you would want to create a computed observable to represent a filtered version of your array.
So, you would have something like:
self.users = ko.observableArray();
self.query = ko.observable();
self.filteredUsers = ko.computed(function() {
var query = self.query();
return ko.utils.arrayFilter(self.users(), function(user) {
return user.FirstName.indexOf(query) > -1 || user.LastName.indexOf(query) > -1;
});
});
I also hink you have to iterate over self.users() instead of users.
users is the observableArray-function while users() provides access to the underlying data.

Categories

Resources