knockout.js not mapping observable array from json - javascript

Can't map json to observable array. I use code from tutorial (http://learn.knockoutjs.com/).
function Movie(data) {
this.name = ko.observable(data.name);
this.description = ko.observable(data.description);
this.duration = ko.observable(data.duration);
this.id = ko.observable(data.id);
this.imdb_id = ko.observable(data.imdb_id);
this.original_name = ko.observable(data.original_name);
this.poster = ko.observable(data.poster);
this.type = ko.observable(data.type);
this.year = ko.observable(data.year);
}
function MovieListViewModel() {
// Data
var self = this;
self.moviesArray = ko.observableArray([]);
self.searchQuery = ko.observable();
self.searchMovies = function () {
$.getJSON("/api/v1/movies/search/", {"query": self.searchQuery }, function(allData) {
var mappedMovies = $.map(allData.movies, function(item) { return new Movie(item) });
console.log(mappedMovies); // in this line output: [Movie, Movie, Movie, Movie, Movie, Movie]
self.moviesArray(mappedMovies);
console.log(self.moviesArray); // in this line output: []
});
};
}
ko.applyBindings(new MovieListViewModel());
I do not understand what is wrong.
P.S. Sorry for my english

This
{"query": self.searchQuery }
Should be
{"query": self.searchQuery() }
This
console.log(self.moviesArray)
Should be
console.log(self.moviesArray())

Related

Create prototypes for subobjects, javascript

Let's say I have this object:
var order = {
customer: 'Joe',
items: [
{
id: 123,
title: 'VCR',
}
]
};
I want to create logic for both the order and the items object. So I need to write a class that instantiate another class.
The Order class:
function Order(customer, items){
this.customer = customer;
this.items = items;
}
Order.prototype = {
getCustomer: function(){
console.log(this.customer);
}
};
The Item class:
function Item(id, title){
this.id = id;
this.title = title;
}
Item.prototype = {
getItemId: function(){
console.log(this.id);
}
};
The instance:
var myOrder = new Order(order.customer, order.items);
Now this is what I want to do:
myOrder.items[0].getItemId() //123
myOrder.items[0].getCustomer() //Joe
How do I make this work? I need to connect the classes somehow and add a prototype to the item objects.
Jsbin: https://jsbin.com/nofofifuka/edit?html,js,console
You can use Array.prototype.map to call the Item constructor on the elements in items.
function Order(order){
this.customer = customer;
this.items = order.items.map(function (item) {
return new Item(item);
});
}
Array.prototype.map calls the given function once for each element in the source array, and returns a new array, of the same length, containing the results of the calling the function.
Alternatively you could make your Item constructor have a few extra smarts so it can instantiate itself if called without new:
function Item(item){
if (! (this instanceof Item)) { return new Item(item); }
this.id = id;
this.title = title;
}
Then you can use it directly in the map:
function Order(order){
this.order = order;
order.items = order.items.map(Item);
}
What you want to achieve has nothing to do with inheritance, I think. Can you check if code below satisfies what you want to achieve?
function Order(id){
this.id = id;
this.items = [];
}
Order.prototype.addItem = function(item) {
this.items.push(item);
};
Order.prototype.getItems = function() {
return this.items;
};
Order.prototype.getItem = function(id) {
for (var i = 0, l = this.items.length; i<l; i++) {
if (this.items[i].id === id) return this.items[i];
}
return;
};
function Item(id, customer) {
this.id = id;
this.customer = customer;
}
var order1 = new Order(1);
order1.addItem(new Item(123, 'customer1'));
order1.addItem(new Item(345, 'customer2'));
order1.addItem(new Item(456, 'customer3'));
console.log(order1.getItems());
console.log(order1.getItem(345));
On my glance, such case should reflect the following situation in real life:
Certain buyer(customer) makes an order which may contains many product positions (items). That items have their unique characteristics and should added separately into the order.
The Order class:
function Order(customer){
this.customer = customer;
this.items = [];
}
Order.prototype = {
constructor: Order,
addItem: function(id, title){
this.items.push(new Item(id, title));
},
getCustomer: function(){
console.log(this.customer);
}
};
The Item class:
function Item(id, title){
this.id = id;
this.title = title;
}
Item.prototype = {
constructor: Item,
getItemId: function(){
console.log(this.id);
}
};
The instance:
var myOrder = new Order(order.customer);
order.items.map(function (item) {
myOrder.addItem(item.id, ite.title);
});
myOrder.items[0].getItemId() //123
Array.prototype.map is the right tool, but in order to deliver the logic you ask for, I think it should be used more like this:
function Order(o){
this.items = o.items.map(function(obj){
obj.customer = o.customer;
var r = obj;
r.getItemId = function(){ console.log(obj.id); };
r.getItemTitle = function(){ console.log(obj.title); };
r.getCustomer = function(){ console.log(obj.customer); };
return r;
});
}
var myOrder = new Order(order);
myOrder.items[0].getCustomer();
myOrder.items[0].getItemId();
myOrder.items[0].getItemTitle();
Considering it is obvious from the code, I didn't add above, but thanks to line 4 (var r = obj;) you can use customer, id, and title properties of myOrder.items[n] as well as all getters.

Cloning an object with Knockout that has an observable array

I have a view model called ProductsViewModel
This contains an observableArray of ProductViewModel
A ProductViewModel also contains an observableArray - of ProductPriceViewModel
One feature I have is that I can duplicate a ProductViewModel and insert it into the ProductsViewModel array.
When I clone using:
ko.mapping.fromJS(ko.toJS(itemToCopy));
It doesn't appear to copy correctly - the prices observable array, isn't populated with ProductPriceViewModels - just Object
Here's the view models
var ProductsViewModel = function() {
var self = this;
self.products = ko.observableArray([new ProductViewModel()]);
self.addNewProduct = function() {
self.products.push(new ProductViewModel());
};
self.duplicateProduct = function() {
var itemToCopy = ko.utils.arrayFirst(self.products(), function(item) {
return item.visible();
});
//if i look at itemToCopy.prices() it is an array of ProductViewModel
var newItem = ko.mapping.fromJS(ko.toJS(itemToCopy));
//if i look at newItem.prices() it is an array of Object
self.products.push(newItem);
};
};
var ProductViewModel = function() {
var self = this;
self.name = ko.observable();
self.visible = ko.observable(true);
self.prices = ko.observableArray([new ProductPriceViewModel()]);
self.addPrice = function() {
self.prices.push(new ProductPriceViewModel());
};
};
var ProductPriceViewModel = function() {
var self = this;
self.name = ko.observable();
self.price = ko.observable();
};
I solved this by passing in a mapping configuration like this:
var mapping = {
'prices': {
create: function (options) {
return new ServicePriceViewModel(options.data);
}
}
};
on
var newItem = ko.mapping.fromJS(ko.toJS(productToCopy), mapping);
and changing my ProductPriceViewModel to accept data as a parameter:
var ProductPriceViewModel = function (data) {
var self = this;
self.name = ko.observable();
self.description = ko.observable();
self.price = ko.observable();
self.priceIsFrom = ko.observable();
if (data)
ko.mapping.fromJS(data, {}, this);
};

JavaScript Closures Manipulation

I'm doing some Node.js and I want to use the closure representation to create my objects. I think I'm missing something, because something simple like this isn't working:
var Room = function(foo) {
this.name = foo;
this.users= [];
return {
getName : function() {
return this.name;
}
}
}
var room = new Room("foo");
console.log(room.getName());
I also have tried without the parameter.. and still not working.
var Room = function() {
this.name = "foo";
this.users= [];
return {
getName : function() {
return this.name;
}
}
}
var room = new Room();
console.log(room.getName());
However, something like this works:
var Room = function(foo) {
this.name = foo;
this.users= [];
}
var room = new Room("foo");
console.log(room.name);
I can't understand why this isn't working.
--Edited
Thanks to Amadan I have found the right way to do it:
var Room = function() {
var name = "foo";
var users= [];
return {
getName : function() {
return name;
}
}
}
var room = new Room();
console.log(room.getName());
This way "name" and "users" are encapsulated.
return in a constructor will overwrite this. So the right way to do this is:
var Room = function(foo) {
this.name = foo;
this.users= [];
this.getName = function() {
return this.name;
}
}
or
var Room = function(foo) {
return {
name: "foo",
users: [],
getName : function() {
return this.name;
}
}
}
The first one does everything on the original this; the second one replaces this with everything you need.

Why angular factory not working as expected

Below is simple angular factory which almost works. I want to reset my model and I tried to add a method called 'resetModel' but it doesn't work as It is not resetting the model properties. Could someone please explain why?
app.factory('programLocationModel', [ "$rootScope", function ($rootScope)
{
var ProgramLocationModel = function()
{
this.name = "All Programmes";
this.description = "";
this.category = "";
this.series = {};
this.channel = {};
this.duration = "";
this.airTime = "";
this.seriesName = "";
this.url = "../assets/images/nhkw_thumbnail.jpg"; //Default client logo
}
ProgramLocationModel.prototype.update = function( data )
{
this.name = data.name;
this.description = data.description;
this.category = data.category;
this.series = data.series;
this.seriesName = data.seriesName;
this.channel = data.channel;
this.duration = data.duration;
this.airTime = data.airTime;
this.url = $rootScope.resturl + '/graph/' + data.id + '/thumbnail?access_token=' + $rootScope.token;
}
ProgramLocationModel.prototype.resetModel = function () {
ProgramLocationModel();
}
return new ProgramLocationModel();
} ] );
Your resetModel function is only calling the constructor and not doing anything to the actual instance the method is called on. Your resetModel function is supposed to modify the properties of this, just like you already do in the constructor and in your update method.
Here is a simple way to do it:
app.factory('programLocationModel', [ "$rootScope", function ($rootScope)
{
var ProgramLocationModel = function()
{
this.resetModel();
}
ProgramLocationModel.prototype.update = function( data )
{
this.name = data.name;
this.description = data.description;
this.category = data.category;
this.series = data.series;
this.seriesName = _seriesName;
this.channel = data.channel;
this.duration = data.duration;
this.airTime = data.airTime;
this.url = $rootScope.resturl + '/graph/' + data.id + '/thumbnail?access_token=' + $rootScope.token;
}
ProgramLocationModel.prototype.resetModel = function () {
this.name = "All Programmes";
this.description = "";
this.category = "";
this.series = {};
this.channel = {};
this.duration = "";
this.airTime = "";
this.seriesName = "";
this.url = "../assets/images/nhkw_thumbnail.jpg"; //Default client logo
}
return new ProgramLocationModel();
} ] );

JavaScript Inheritance or How is this Working?

I'm trying to achieve a degree of inheritance in JavaScript and here is what I have so far:
function Address() {
this.address1 = ko.observable();
this.address2 = ko.observable();
this.country = ko.observableSafe(null, new Country(-1, '', false));
this.city = ko.observable('');
this.state = ko.observable();
this.province = ko.observable('');
this.zipCode = ko.observable();
this.countryLookupID = '';
this.stateLookupID = '';
ko.computed(function () {
var newCountry = this.country();
if (newCountry) {
this.countryLookupID = newCountry.id.toString();
if (newCountry.international) {
this.clearDomestic();
}
else {
this.clearInternational();
}
}
else {
this.countryLookupID = "";
}, this);
ko.computed(function () {
var newState = this.state();
if (newState) {
this.stateLookupID = newState.id.toString();
}
else {
this.stateLookupID = "";
}
}, this);
}
Address.prototype.clearDomestic = function () { return true; };
Address.prototype.clearInternational = function () { return true; };
function Company() {
this.base = Address;
this.base(this);
this.legalEntityID = ko.observable(0);
this.legalEntityName = ko.observable('');
this.isFemaleOwned = ko.observable(false);
this.isMinorityOwned = ko.observable(false);
this.webAddress = ko.observable();
this.businessPhone = ko.observable();
this.faxNumber = ko.observable();
this.locked = ko.observable(false);
}
Company.prototype.constructor = Address;
Company.prototype.clearDomestic = function () {
this.businessPhone('');
this.state(null);
this.zipCode('');
};
Company.prototype.clearInternational = function () {
this.province('');
};
If you are unfamiliar with the Knockout framework, that is OK, as it's probably not pertinent to this discussion. I haven't seen inheritance done exactly like this anywhere that I've looked. As it currently stands, this works exactly how you think it should. When clearDomestic() is called, the correct version of the function is called in the inherited class and this points to a Company object. If I take out the base and call to base(this), it breaks.
Can anyone explain why this is working? If this is a bad practice, can someone tell me how to rewrite it so it functions the same? I don't really want to include another library to achieve this.
UPDATE
If inside Address you invoke this.clearDomestic() outside of the ko.computed, it tries to call the clearDomestic() attached to Company but then this points to an Address object and so businessPhone is no longer defined.
UPDATE 2
I've moved things around again, and I've settled on this method. It's not ideal, but it's the only way that consistently works.
function Address() {
this.address1 = ko.observable();
this.address2 = ko.observable();
this.country = ko.observableSafe(null, new Country(-1, '', false));
this.city = ko.observable('');
this.state = ko.observable();
this.province = ko.observable('');
this.zipCode = ko.observable();
this.countryLookupID = '';
this.stateLookupID = '';
}
Address.prototype.clearDomestic = function () { return true; };
Address.prototype.clearInternational = function () { };
function Company() {
this.legalEntityID = ko.observable(0);
this.legalEntityName = ko.observable('');
this.isFemaleOwned = ko.observable(false);
this.isMinorityOwned = ko.observable(false);
this.webAddress = ko.observable();
this.businessPhone = ko.observable();
this.faxNumber = ko.observable();
this.locked = ko.observable(false);
ko.computed(function () {
var newCountry = this.country();
if (newCountry) {
this.countryLookupID = newCountry.id.toString();
if (newCountry.international) {
this.clearDomestic();
}
else {
this.clearInternational();
}
}
else {
this.countryLookupID = "";
}
}, this);
ko.computed(function () {
var newState = this.state();
if (newState) {
this.stateLookupID = newState.id.toString();
}
else {
this.stateLookupID = "";
}
}, this);
}
Company.prototype = new Address;
Company.prototype.clearDomestic = function () {
// Since we are entering this method via Address, we need a reference back to a real company object with self.
this.businessPhone('');
this.state(null);
this.zipCode('');
};
Company.prototype.clearInternational = function () {
this.province('');
};
I'm going to have to do the logic in the newstate and newcountry in every object that inherits from Address which makes this far from ideal, but until I find a better suggestion, I'm stuck with this.
I'm not sure I understand the problem exactly, but instead of this.base(this);, try calling this.base.call(this);? (in the first version of your code).

Categories

Resources