Get JSON array from Ext.data.Store outside? - grid

How can I get JSON array from Ext.data.Store outside the function?
The code:
var store = new Ext.data.Store({
model: 'nested' + type,
proxy: {
type: 'ajax',
url: '/Grid/GetDetailed?InvoiceId=' + $(row).attr('id'),
reader: {
type: 'json',
root: 'items',
totalProperty: 'totalCount'
}
}
});
store.load();
And I want to use something like this:
store.getAt(0);
but it's undefined.
someone said it beacaouse of the ajax which is asynchronic.

If you use store.getAt(0) immediately after the store.load() is called then yes, the problem is that the load is asynchronic so you should use the callback method of the load to fix this.
store.load({
scope : this,
callback: function(records, operation, success) {
//here the store has been loaded so you can use what functions you like
store.getAt(0);
}
});

Use Ext.create instead of new keyword when creating the store, and define a storeId for the store. Then you can use Ext.getStore() method to retrieve the store.

You can also make it work by doing the following:
//This function will be called only after the store has been loaded successfully.
store.on('load',function(this, records, successful, eOpts){
store.getAt(0);
}, this);

Related

How do I handle values that are dependent on the result of a helper?

So I have an Angular controller with a meteor helper method, as below.
function localeCtrl($scope, $reactive, $stateParams{
$reactive(this).attach($scope);
var self = this;
self.helpers({
locale: function(){ return Locales.findOne($stateParams.id)},
staff: function(){
// Load data from second collection based on current Locale.
// But how?
},
address: function(){
// Take self.location.address and massage it to provide
// google maps link. How?
}
tags: function(){
// Collect all unique instances of a given tag by
// iterating over the available locales.
// E. G. If 10 locales have the 'restaurant' tag, and 5
// more have the 'library' tag, I want an array of
// ['restaurant', 'library'] -- easy enough to do
// by iterating over the locales, but how do I do that
// reactively?
}
});
}
Unfortunately, I need to set additional properties based on the data fetched by locale(). I can't set them up when I initialize the controller because the value in locale() changes as data is fetched from the server. But I need access to the data in locale to, for example, create the google maps address, or fetch associated records. (They aren't imbedded in the locale document for reasons that I'm sure made sense at the time).
Edit:
Additionally, I'm using ground DB to store a local copy of the data for offline access, which makes life even more complicated.
Probably you best bet is to publish your collection using publishComposite which is implemented using the reywood:publish-composite package.
Add the package:
meteor add reywood:publish-composite
Now where you publish the Locales collection you would do something like this:
Meteor.publishComposite('locales', function() {
return {
find() {
//Put whatever you need in the query for locales
const query = {
_userId: this.userId
};
return Locales.find(query);
},
children: [{
find(locale) {
return Staff.find({ localeId: locale._id });
}
}]
};
});
Then in your controller before the helper you add this:
this.subscribe('locales');
Now you should be able to simply call the code like this:
this.helpers({
locale(){
return Locales.findOne(this.$stateParams.id);
}
});
And access it in the template like this:
locale.staff
Give that a try and let me know!

dynamic query object for iron-router route

I have a current route as follows:
Router.route('/:arg', function(){
this.render('tmpResults', {
data: function(){
return hcDrefs.find({name: {$regex: this.params.query.q}})
}
});
}, {name: 'showSome'});
As you can see, the query is hard coded to really only take a single input (see block below). What I want to do is execute this route where the query object would need to be of a variable form...sometimes I only need a simple query, other times, the query may contain an AND operation. I currently call this route with:
Router.go('showSome', {arg: 1},
{query: '?q=' + e.target.value});
}
...what I want to know is if I can pass some kind of query object to the route somehow, but I haven't seem to hit on a syntax that work....not even sure IF it can work. I have tried some brute force stuff like defining a query object:
query_object = {name: {$regex: pattern}}
and attempting to get it to the router somehow like:
Router.go('showSome', {arg: 1},
{query: query_object});
}
//----------------------------------------------------------
Router.route('/:arg', function(){
this.render('tmpResults', {
data: function(){
return hcDrefs.find(this.params.query.q}})
}
});
}, {name: 'showSome'});
but that seems to be a no go.
What would be a good way to set the data context of a route where the query to yield the data context could be of a variable form? I would think a global object would probably work, but I am curious is there is a more strategic way passing through the router. I am still digging through the iron router docs for something I can hook onto.
There is no client-side POST, so the data you can pass to a client-side route is limited by what you can put into the URL (similar to a GET request). So, no, I don't think there is a more elegant solutions.
I think you have two options:
Stringifying your object into the URL:
Router.go('showSome', {arg: 1},
{query: JSON.stringify(query_object)});
//----------------------------------------------------------
Router.route('/:arg', function(){
this.render('tmpResults', {
data: function(){
return hcDrefs.find(JSON.parse(this.params.query)}})
}
});
}, {name: 'showSome'});
2) use a global or session variable:
Session.set('query', query_object);
Router.go('showSome', {arg: 1});
//----------------------------------------------------------
Router.route('/:arg', function(){
this.render('tmpResults', {
data: function(){
return hcDrefs.find(Session.get('query'))}})
}
});
}, {name: 'showSome'});

Meteor client side collection needs to have all data populated before anything else

I'm trying to use a client side collection as a site configuration system. I insert documents representing my different pages, and the iron-router and navigation tabs all use them to determine what pages they are and what templates are represented by them. Each page uses a {{> contentTemplate}} inclusion helper to load it's relevant template.
It all works great, when the data has all loaded. When I restart the app on certain pages, the data hasn't loaded yet, and I receive the Exception from Deps recompute function: Error: Expected null or template in return value from inclusion function, found: undefined error.
Here's my javascript:
StoriesArray = [
{ category: 'teaching', contentTemplate: 'teachingHome', title: 'Teaching Home'},
...
];
Stories = new Meteor.Collection(null);
StoriesArray.forEach(function (story, index) {
story._id = index + '';
Stories.insert(story);
});
// in main.js
Template.teachingPost.contentTemplate = function() {
console.log(this);
console.log(this.contentTemplate);
return Template[this.contentTemplate];
};
// in router.js
this.route('teaching', {
layoutTemplate: 'teachingPost',
data: function() { return Stories.findOne({contentTemplate: 'teachingHome', category: 'teaching'}); }
});
The console logs in the contentTemplate helper above log twice, the first time as this:
Object {} main.js?1f560c50f23d9012c6b6dd54469bb32b99aa4285:45
undefined main.js?1f560c50f23d9012c6b6dd54469bb32b99aa4285:46
and the second time as this:
Object {category: "teaching", contentTemplate: "teachingHome", title: "Teaching Home"} main.js?1f560c50f23d9012c6b6dd54469bb32b99aa4285:45
teachingHome main.js?1f560c50f23d9012c6b6dd54469bb32b99aa4285:46
so the router is simply trying to load this data too early.
I've tried putting the StoriesArray loading process into different files all over my app, including lib, and even tried putting it into Meteor.startup, but it's always the same result.
The normal iron-router waitOn/subscription pattern doesn't really apply here, since this is a client side collection built with null, that has no server representation. I don't want this to have server representation, because this is static content that there's no need to go to my server for.
How do I ensure this information is done before continuing?
Untested, but per Iron Router's docs on waitOn:
Returning a subscription handle, or anything with a ready method from the waitOn function will add the handle to a wait list.
Also in general it's better to use find with data, rather than findOne, as find will return an empty cursor when the collection is empty as opposed to findOne returning undefined. So try this:
// in router.js
this.route('teaching', {
layoutTemplate: 'teachingPost',
data: function() {
return Stories.find({contentTemplate: 'teachingHome', category: 'teaching'});
},
waitOn: function() {
var handle = {};
handle.ready = function() {
if (Stories.find().count() !== 0)
return true;
else
return false;
}
return handle;
}
});
And adjust your Template.teachingPost.contentTemplate function to work with a cursor rather than an object.

How do I use ObjectID from a mongoimport?

I use a mongoimport to import a bunch of large csv files into a meteor collection, however when they do the insertions the _id values are ObjectID, whereas meteor uses string ids. There's a small blurb on ObjectIDs in the meteor docs here but I don't really understand what I am supposed to do. For example, using Iron Router I have a single route like so
this.route('profileView', {
path: '/profiles/:_id',
notFoundTemplate: 'notFound',
fastRender: true,
waitOn: function() {
return [Meteor.subscribe('singleProfile', this.params._id, Meteor.userId())];
},
data: function() {
Session.set('currentProfileId', this.params._id);
return Profiles.findOne({
_id: this.params._id
}, {
fields: {
submitted: 0
}
});
}
but the url of the route is of type object and looks like http://localhost:3000/profiles/ObjectID(%22530845da3621faf06fcb0802%22). It also doesn't return anything and the page renders blank. Here's the publication.
Meteor.publish('singleProfile', function(id, userId) {
return Profiles.find({
_id: id,
userId: userId,
forDel: {
$ne: true
}
});
});
I guess my question is, how am I supposed to use ObjectIDs so that the routes use just the string portion of the ObjectID, and how do I return the data properly?
Update: I've managed to get the ObjectID out of the url by changing the link to the view from Details to Details so the url is now http://localhost:3000/profiles/530845da3621faf06fcb0802. Unfortunately, the page still renders blank, and I am not sure if that's because of the way I am subscribing, publishing, or finding the collection item.
Summing up the comment thread as an answer:
The string part of the ObjectID can be obtained by simply calling ._str on the id as
id._str
You can also craft an ObjectID from a hex string by using
new Meteor.Colletion.ObjectID(hexstring)
So when you access your route using Details you can craft your find like:
Profiles.findOne({
_id: new Meteor.Collection.ObjectID(this.params._id)
});
Generally speaking, when working with ObjectID's, you will find yourself needing some antipatterns to convert from string to objectId or vice versa, so a utility like the following will come in handy:
IDAsString = this._id._str ? this._id._str : this._id
and
IDAsObjectId = this._id._str ? this._id : new Meteor.Collection.ObjectID(this._id)
Please also take a look at github.com/meteor/meteor/issues/1834 and groups.google.com/forum/#!topic/meteor-talk/f-ljBdZOwPk for pointers and issues around using ObjectID's.

backbone.js collection not responding to .each

I have what should be very simple. I create a new collection, and I want to pass it to a render and add the collection models to the page.
get_results: function(){
$.getJson(this.url,function(response){
this.search_results = new Kitchon.Collections.searchList(response);
console.log(this.search_results);
this.search_results.each(this.render_match);
}
},
render_match: function(model){
console.log(model)
},
This returns an error
Uncaught TypeError: undefined is not a function
my collection has an ordinary structure
_byCid: Object
_byId: Object
_onModelEvent: function () { [native code] }
_removeReference: function () { [native code] }
length: 7
models: Array[7]
__proto__: o
I've tried LOTS of things, but one thing that stuck out was maybe I had to pass
this.search_results.models.each(this.render_match); as that is the actual array, but if I do that I get a Uncaught typeError: Object [object Object],[object Object],...
you lose the execution context when callback function for each method is called
use _.bind(this.render_match, this) when passing callback to make sure that render_match has the right context
and you were getting error because you didn't wrap the callback function for getJson method neither. You have to use underscore bind method there as well.
You should read a bit about javascript this and how to tame it - try here http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/
Correct code should look more or less like this...
get_results: function(){
$.getJSON(this.url, _.bind(function(response){
this.search_results = new Kitchon.Collections.searchList(response);
console.log(this.search_results);
this.search_results.each(_.bind(this.render_match, this));
}, this));
},
render_match: function(model){
console.log(model)
},
Though from what I seee - I assume the code you've shown here is either a model or collection - is handling rendering the view - you shouldn't do that! Models and Collections are only to store and parse data - all rendering and controlling application flow should be done in the View(Controllers) with help of the Router.
$.getJson changes the this reference. Many methods in jquery do that, so the value of this.render_match is null. You pass null to each and it fails.
To solve that, create a reference to this (like var _this = this;) before $.getJson and use it instead of this. Code should be like below:
get_results: function(){
var _this = this;
$.getJson(this.url,function(response){
_this.search_results = new Kitchon.Collections.searchList(response);
console.log(_this.search_results);
_this.search_results.each(_this.render_match);
}
},
render_match: function(model){
console.log(model)
},
Just taking a stab here (I don't know anything about Backbone.js), but isn't this what you are looking for:
$.each(this.search_results, function(index, value) {
alert(index + ': ' + value);
});
Good Luck!

Resources