How can I fill observableArray data obtained from yii2. Let's say I have a static json array in knockout application:
var countries = [{"id": 1, "name": "1"}, {"id": 2, "name": "2"}, {"id": 3, "name": "3"}]
When I do this:
var VM = function(){
this.countries = ko.observableArray(countries);};
It's all good. And I can continue to use it for HTML-tag select. But when I try to retrieve data from the server it does not work. Here's how I'm trying to do.
The data from the server:
$d = Countries::find()->all();
$r = Yii::$app->response();
$r->format = Response::FORMAT_JSON;
$r->data = $d;
return $r;
Knockout application code:
var VM = function(){
this.countries = ko.pureComputed(function(){$.getJSON("/index.php?r=countries/index").done(function(data){return data;});});};
If you watch from a browser(/index.php?r=countries/index), the array looks correct:
[{"id": 1, "name": "1"}, {"id": 2, "name": "2"}, {"id": 3, "name": "3"}]
But I can't figure out how to bind to observable array. How do I get data from the server and make them as observableArray.
A little race started. In my previous question I learned using knockoutjs to bind multiple lists by ID. The next step was to get the lists from the server (from json yii2). It seemed that everything should be very simple, but the anything do not work. Precisely here the question arose I decided to ask the local community. I searched so long for, was thinking how to do it, tried different options. Finally I found while initializing the app to load the list. Something like this:
var C = function(){
this.t = ko.observableArray();
var loadFrom = function(){
$.getJSON("someUrl", function(data){
$.each(data, function(index, value){
t.push({'id': value.id, 'name': value.name});
});
});
};
}
var VM = function(){
}
$(function(){
ko.applyBindings(new VM());
C.loadFrom;
})
It works! To initialize this method is suitable. But what if I need to load the list data at runtime of the application. To think again! Ohhh no! And then I found a great post by Christos S. He offers to bind ViewModel to a method in the following way:
<select data-bind:"optionsValue: 'AlbumId', event: { change: function (data, event) { showAlbumTracks($('#selectArtistAlbums').val(), data, event) } }"></select>
This is what I need! But I did not like that very cluttered HTML. But then I remembered one method of living examples on the knockout website.
self.category.subscribe(function() {
self.product(undefined);
});
All now I am satisfied. Well, almost... I don't like the solution with calling any methods after ko.applyBindings(). Can someone tell me how to make it better. The answer was confusing. Later I plan to put this on jsfiddle and share with the community.
Related
The topic of this post is: my solution is too slow for a large query return.
I have a Web Api serving REST results like below from a call to localhost:9090/api/invetories?id=1:
[
{
"inventory_id": "1",
"film_id": "1",
"store_id": "1",
"last_update": "2/15/2006 5:09:17 AM"
},
{
"inventory_id": "2",
"film_id": "1",
"store_id": "1",
"last_update": "2/15/2006 5:09:17 AM"
}
]
Since my WebAPI did not provide a root key for my JSON response, I made a RESTSerializer like following.
export default DS.RESTSerializer.extend({
extract:function(store,primaryType,payload,id,requestType){
var typeName = primaryType.typeKey;
var data = {};
data[typeName] = payload; // creating root
payload = data;
return this._super(store,primaryType,payload,id,requestType)
}
});
When this gets run, I get the following error message: Assetion failed: You must include an 'id' for inventory in an object passed to 'push'
As you can see, these objects do not have the attribute id, so I found that the default behaviour of Ember RESTSerializer forces me to write my own.
Okay, so here's where I'm not sure my solution is right. inventory_id from my return is unique, therefore I choose to use that as an id, okay I'm thinking to my self, I'll just add it manually. The function looks like this now:
export default DS.RESTSerializer.extend({
extract:function(store,primaryType,payload,id,requestType){
var typeName = primaryType.typeKey;
for(var i=0;i<payload.length;i++){
payload[i].id = payload[i].inventoryId;
}
var data = {};
data[typeName] = payload; // creating root
payload = data;
return this._super(store,primaryType,payload,id,requestType)
}
});
By just manually duplicating an attribute, I feel like I'm cheating my way over this error message. In addition, I sometimes return a large payload array (over 150k rows). Looping O(n) just doesn't seem a right price to pay for just a simple mapping.
Is there some other way to set either my WebAPI or serializer up so I avoid the for loop in assigning the id that ember so desperately wants.
I think this should fix your problem:
export default DS.RESTSerializer.extend({
primaryKey: 'inventory_id'
});
With this parameter Ember Data will map inventory_id to it's id parameter.
I have a json file called data.json containing some data:
[
{
"name" : "toto"
}
]
And the script I wrote to manage it :
var Data = $resource("data.json", {},
{
query: {method:'GET',isArray:true}
});
var data = Data.query(function()
{
var d = data[0];
d.name = "Titi";
d.$save();
});
Everything work before I call $save() on my object. I have this error:
[11:30:24.962] "Error: [$resource:badcfg] Error in resource configuration.
Expected response to contain an object but got an array
I don't really know the problem. I have already read many examples and documentations but this does not seem clearer to me.
Just guessing to help quickly here... You may have several issues, var d = data[0]. data should be from an argument/parameter of the function not the var data which is null until the return result from Data.query changes it. $save should also likely be on the $resource object, not the object in the array. Not an angular person, but guessing that might be the case.
The problem
I'm trying to filter json data and display only a portion of it on an Angular page, based on the page's current URL.
In detail
I have a list of 100 JSON objects, and each one looks like this:
{
"name": "Evangeline Perreault",
"age_1": 1,
"total_age": 1,
"photo_small": "img/400/001_400.jpg",
"photo_medium": "img/800/001_800.jpg",
"photo_large": "img/1200/001_1200.jpg",
"photo_extralarge": "img/1600/001_1600.jpg",
"video": 67443664,
"id": 1,
"quote": "test quote here and here",
"type": 1
},
The 'type' attribute is what I want to use to filter out the subsets of my data. With that in mind, I tried to setup my URL structure to tie the type attribute here to my url. Here is my route:
angular.module('100_Ages', ['mydirectives', 'ngResponsiveImages']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/100_Ages/nav/:personType', {templateUrl: 'partials/person-list.html', controller: NavListCtrl}).
otherwise({redirectTo: '/100_Ages'});
}]);
So, I have pointed the route to the 'type' field in my JSON and I tried writing a controller to tie the two together.
function NavListCtrl($scope, $routeParams, $http) {
$http.get('person.json').success(function(data) {
angular.forEach(data, function(person) {
if (person.type == $routeParams.personType)
$scope.person = person;
});
});
}
And here is my partial template:
<div class="nav_outer"><img class="nav_img" ng-src="{{person.photo_small}}" ng-alt="{{person.name}}" /></div>
I expected this to display all the matching images for the URL type I'm on. So, if I'm on "/100_Ages/nav/3", I expected all the images (roughly 10 pictures) from the objects with a type of "3" to display. However, it only displayed the last object with a type of "3".
So, I tried an ng-repeat like so:
<div class="nav_outer" ng-repeat="person in persons"><img class="nav_img" ng-src="{{person.photo_small}}" ng-alt="{{person.name}}" /></div>
I expected that to loop through and show all the matching images, but that made nothing at all show up.
I think my problem has to do with the angular.forEach, but I'm not sure how else to tie my JSON type to the page's typeid.
Thanks for any suggestions.
The ng-repeat should work if you push each item into an array. (Also, you are referring to a 'persons' object in the ng-repeat, which doesn't exist according to code provided). So, try this:
$http.get('person.json').success(function(data) {
$scope.persons = [];
angular.forEach(data, function(person) {
if (person.type == $routeParams.personType)
$scope.persons.push(person);
// or alternatively - this.push(person), with the optional 3rd param of $scope.persons (I don't really understand that, but whatever...)
});
});
Now with the array populated, your ng-repeat="person in persons" should work.
UPDATE:
If the success object was already an array of objects, then just set the scope object to the array - no need to iterate through them:
$http.get('person.json').success(function(data) {
$scope.persons = data;
})
I just working with JSON data and am playing around with jQuery and Ajax requests. Pretty basic stuff, but here's my problem.
I have a basic data set which I was using for time tracking. I know how to parse the simple JSON data like this:
{
"end" : "1/18/2011",
"start" : "1/18/2011",
"task" : "Code Review",
},
It's the more complicated stuff I'm trying to parse like this where I'm trying to pull the "time" data out.
{
"end" : "1/17/2011",
"start" : "1/17/2011",
"task" : "Exclusive Brands",
"time" : {
"analysis" : 4,
"documentation" : 3,
"meetings" : 2
}
This is the code for the script I've been using to parse the simple data:
$(function() {
$('.load').click(function(){
$.getJSON("data.js",function(data){
$.each(data.timesheet, function(i,data){
var div_data ="<div class='box'>"+data.start+" "+data.task+"</div>";
$(div_data).appendTo("#time-tracking");
});
}
);
return false;
});
});
My question is what's the format to parse the time data, or what's the best way to parse the information nested inside the time element?
Any help will be greatly appreciated.
A JSON string will be parsed into an object. When parsed, the time is the key of one object. You could retrieve the value of this object through the dot operator (.).
data = JSON.parse('{"end":"1/17/2011", "start":"1/17/2011", "task":"Exclusive Brands", "time": {"analysis":4, "documentation":3, "meetings":2 } }')
// => obj
data.time.analysis
// => 4
In your case similarly you could use the data.time.meetings to access your data from remote server.
Unless I am terribly mistaken, since jquery already converted data into a javascript for you, you should be able to access time as if it was a javascript object like so:
var analysis = data.time.analysis;
var documentation = data.time.documentation;
var meetings = data.time.meetings;
etc...
I've been lurking on Stack Overflow for quite some time now, and have found quite a number of very helpful answers. Many thanks to the community! I hope to be able to contribute my own helpful answers before too long.
In the meantime, I have another issue I can't figure out. I am using Sencha Touch to create a Web-based phone app and I'm having trouble using a nested loop to iterate through some JSON. I can grab the first level of data, but not the items nested within that first level. There is a somewhat related ExtJS thread, but I decided to create my own since ExtJS and Touch diverge in subtle yet important ways. Anyway, here is some code to show where I am:
JSON (truncated - the JSON is PHP/MYSQL-generated, and there are currently actually three sub levels with "title", all of which I can access. It's the sub level "items" through which I can't iterate):
{
"lists": [
{
"title": "Groceries",
"id": "1",
"items": [
{
"text": "contact solution - COUPON",
"listId": "1",
"id": "4",
"leaf": "true"
},
{
"text": "Falafel (bulk)",
"listId": "1",
"id": "161",
"leaf": "true"
},
{
"text": "brita filters",
"listId": "1",
"id": "166",
"leaf": "true"
}
]
}
]
}
Store:
var storeItms = new Ext.data.Store({
model: 'Lists',
proxy: {
type: 'ajax',
method: 'post',
url : LIST_SRC,
extraParams: {action: 'gtLstItms'},
reader: {
type: 'json',
root: 'lists'
}
}
});
Working Loop:
storeItms.on('load', function(){
var lstArr = new Array();
storeItms.each(function(i) {
var title = i.data.title;
lstArr.push(i.data.title);
});
console.log(lstArr);
});
Non-working Nested Loop:
storeItms.on('load', function(){
var lstArr = new Array();
storeItms.each(function(i) {
var title = i.data.title;
var id = i.data.id;
title.items.each(function(l) {
lstArr.push(l.data.text);
});
});
console.log(lstArr);
});
The non-working nested loop gives me the error "Cannot call method 'each' of undefined", in reference to 'title.items.each...'
I suspect this is because I've not set title to be a key to set up a key:value pair, so it just sees a list of strings...but I'm kind of at a loss.
I should mention that the store is populated via two Models that have been associated with one another. I know that the Store can access everything because I am able to do nested iterating via an XTemplate.
Any help will be much appreciated and hopefully returned to the community in kind before too long!'
-Eric
Eric, why the loop?
If your models are associated in the same way that the JSON is nested, then you should just be able to set autoLoad:true on the store, sit back and enjoy.
Anyway, on the assumption that you are needing these arrays for some other unrelated reason, the problem is that you are trying .each on
i.data.title.items
Surely you should be iterating through
i.data.items
Also, if the object is a model, you can use .get() instead of the data object:
var title = i.get('title);
Using new sencha touch 2 framework, you can create associations within the models exactly the same way how your json is returned.
Check Sencha Touch 2 Model Document which tells you the various config options on Model.
You may refer to this example of ST2 Nested List .
Hope this helps.
"title" is not a enumerable object, its a string. To iterate a string you'll need to split it to convert it into an array.
Also, instead of using Ext.each try a simple for (var x in obj) {} or for (var xc in obj.prop) {} If that works then the ext.each method should work as well but if ext cannot iterate the object it will just quietly fail.