I am reading in several places that I should be able to get the data context of the current template with Template.currentData();
I found that it seems to only work within an autorun. But after logging the data as a variable there, first it logs null, then it logs the data in the console.
When I try to use the data, like for example trying to pass data._id into a subscription, I get a TypeError in the console. TypeError: Cannot read property '_id' of null. So for some reason, the data is null and I am struggling to find out why.
I have the data context set within my routes using Iron Router:
Router.route('/stock/:stockNumber', {
name: 'stock.detail',
template: 'StockDetail',
data: function () {
return Stock.findOne({
stockNumber: this.params.stockNumber*1
});
}
});
What I am trying to do is get access to the data context so that I can pass some things from it, such as the '_id' into some other subscriptions. What am I doing wrong?
The template is otherwise correctly displaying the data on the page as expected, and I can use Spacebars to show things like {{_id}} for example. But again, I seem to be unable to get access to the data context in Template.StockDetail.onCreated
Ok, so here's what I ended up doing...
Apparently the data context is just simply not available in the onCreated, period. What I had to do was do a Collection.findOne() within the autorun to find the stockItem and set the result to a variable, then use the stockItem._id as the parameter in the new subscription IF the item was found. With both of these things, it seems to work just fine.
Template.StockDetail.onCreated(function () {
let instance = this;
instance.autorun(function () {
instance.subscribe('stock_item', Router.current().params.stockNumber);
let stockItem = Stock.findOne({ // This is what was needed for some reason...
stockNumber: Router.current().params.stockNumber*1
});
if (stockItem) { // ...and, also, this was needed
instance.subscribe('stock_item_scan_log', stockItem._id);
}
});
});
I just don't understand why I can't just easily get the _id some other way. This way just feels incorrect and I don't like it.
Related
For my Flutter app I'm trying to access a List of Notebooks, this list is parsed from a file by the Api. I would like to avoid reading/parsing the file every time I call my notebooks getter, hence I would like something like this:
if the file had already been parsed once, return the previously parsed List<Note> ;
else, read and parse the file, save the List<Note> and returns it.
I imagine something like this :
List<Notebook> get notebooks {
if (_notebooks != null) {
return _notebooks;
} else {
return await _api.getNotebooks();
}
}
This code is not correct because I would need to mark the getter async and return a Future, which I don't want to. Do you know any solution for this problem, like caching values ? I also want to avoid initializing this list in my app startup and only parse once the first time the value is needed.
you can create a singleton and you can store the data inside a property once you read it, from next time onwards you can use the same from the property.
You can refer this question to create a singleton
How do you build a Singleton in Dart?
I get this code in my Meteor project, in a client/main.js file
Template.panel.onCreated(function loginOnCreated() {
var profile = Session.get('profile');
this.myvar = new ReactiveVar(User.find({}).fetch());
});
And the result of User.find({}) is empty. If I Query this anywhere else (including meteor mongo) I get an Array of users.
So I wonder if it is a problem with the fact that this code is running in client side. In this same file I get this query working in other places, but probably in the server context.
How can I populate this ReactiveVar with the Mongo result as soon as the Template/page is loaded?
If I do something like in Meteor.startup() at Server side:
console.log(User.find({}).count());
It gives me the correct number of Users. Immediately.
#edit
If I just add a setTimeout of a few seconds (it can't be jsut 1 second, it needs a longet time), it works in this very same place.
Template.panel.onCreated(function loginOnCreated() {
//...
setTimeout(function(){
template.timeline.set(User.find({}).fetch());
console.log(timeline)
},3000);
});
So, anyone knows why it takes so long to allow me to do this operation? Any workaround?
User.find({}).fetch() will give list of users on server side only.
You can probably write a meteor method for fetching the user list on server side and give it call using meteor.call.
In the callback function to this call you can assign the result to desired variable.
How can I set undefined or null and make empty to a date field in the parse.com data browser.
There is nothing related with code, in the pic i attached, you can see the parse.com data browser and i want to set undefined or null whatever and make that field empty but whenever i tried and double click the cell, data browser open date widget and it is not allowed to delete the data or there is no button to make empty.
Parse.com Data Browser
In data-browser I don't think it's possible to delete a date value, I couldn't find a way to do it at least.
And now that Parse.com will not be maintained anymore(in 01/01/2017 it will shut down), I don't think this may come up as a feature.
My solution to this problem was to make a request to this register and use unset. It gets a bit more of work but solves the problem.
I don't know if you're using parse with Javascript but it can be really easy to adapt to other enviroments.
In Javascript this script should do the trick:
var DateUpdate = Parse.Object.extend("MyTableOnParse");
var dateUpdateEdit = new Parse.Query(DateUpdate);
dateUpdateEdit.equalTo("objectId",myObjectId);
dateUpdateEdit.first({
success: function(object)
{
object.unset("saleDate");
object.save({
success: function(updatedObject)
{
console.log("Data deleted");
}
})
}
});
I am a new user to JavaScript and the meteor framework trying to understand the basic concepts. First of all I want to add a single document to a collection without duplicate entries.
this.addRole = function(roleName){
console.log(MongoRoles.find({name: roleName}).count());
if(!MongoRoles.find({name: roleName}).count())
MongoRoles.insert({name: roleName});
}
This code is called on the server as well as on the client. The log message on the client tells me there are no entries in the collection. Even if I refresh the page several times.
On the server duplicate entries get entered into the collection. I don't know why. Probably I did not understand the key concept. Could someone point it out to me, please?
Edit-1:
No, autopublish and insecure are not installed anymore. But I already published the MongoRoles collection (server side) and subscribed to it (client side). Furthermore I created a allow rule for inserts (client side).
Edit-2:
Thanks a lot for showing me the meteor method way but I want to get the point doing it without server side only methods involved. Let us say for academic purposes. ;-)
Just wrote a small example:
Client:
Posts = new Mongo.Collection("posts");
Posts.insert({title: "title-1"});
console.log(Posts.find().count());
Server:
Posts = new Mongo.Collection("posts");
Meteor.publish(null, function () {
return Posts.find()
})
Posts.allow({
insert: function(){return true}
})
If I check the server database via 'meteor mongo' it tells me every insert of my client code is saved there.
The log on the client tells me '1 count' every time I refresh the page. But I expected both the same. What am I doing wrong?
Edit-3:
I am back on my original role example (sorry for that). Just thought I got the point but I am still clueless. If I check the variable 'roleCount', 0 is responded all the time. How can I load the correct value into my variable? What is the best way to check if a document exists before the insertion into a collection? Guess the .find() is asynchronous as well? If so, how can I do it synchronous? If I got it right I have to wait for the value (synchronous) because I really relay on it.
Shared environment (client and server):
Roles = new Mongo.Collection("jaqua_roles");
Roles.allow({
insert: function(){return true}
})
var Role = function(){
this.addRole = function(roleName){
var roleCount = Roles.find({name: roleName}).count();
console.log(roleCount);
if(roleCount === 0){
Roles.insert({name: roleName}, function(error, result){
try{
console.log("Success: " + result);
var roleCount = Roles.find({name: roleName}).count();
console.log(roleCount);
} catch(error){
}
});
}
};
this.deleteRole = function(){
};
}
role = new Role();
role.addRole('test-role');
Server only:
Meteor.publish(null, function () {
return Roles.find()
})
Meteor's insert/update/remove methods (client-side) are not a great idea to use. Too many potential security pitfalls, and it takes a lot of thought and time to really patch up any holes. Further reading here.
I'm also wondering where you're calling addRole from. Assuming it's being triggered from client-side only, I would do this:
Client-side Code:
this.addRole = function(roleName){
var roleCount = MongoRoles.find({name: roleName}).count();
console.log(roleCount);
if (roleCount === 0) {
Meteor.call('insertRole', roleName, function (error, result) {
if (error) {
// check error.error and error.reason (if I'm remembering right)
} else {
// Success!
}
});
}
}
How I've modified this code and why:
I made a roleCount variable so that you can avoid calling MongoRoles.find() twice like that, which is inefficient and consumes unneeded resources (CPU, disk I/O, etc). Store it once, then reference the variable instead, much better.
When checking numbers, try to avoid doing things like if (!count). Using if (count === 0) is clearer, and shows that you're referencing a number. Statements like if (!xyz) would make one think this is a boolean (true/false) value.
Always use === in JavaScript, unless you want to intentionally do a loose equality operation. Read more on this.
Always use open/closed curly braces for if and other blocks, even if it contains just a single line of code. This is just good practice so that if you decide to add another line later, you don't have to then wrap it in braces. Just a good practice thing.
Changed your database insert into a Meteor method (see below).
Side note: I've used JavaScript (ES5), but since you're new to JavaScript, I think you should jump right into ES6. ES is short for ECMAScript (which is what JS is based on). ES6 (or ECMAScript 2015) is the most recent stable version which includes all kinds of new awesomeness that JavaScript didn't previously have.
Server-side Code:
Meteor.method('insertRole', function (roleName) {
check(roleName, String);
try {
// Any security checks, such as logged-in user, validating roleName, etc
MongoRoles.insert({name: roleName});
} catch (error) {
// error handling. just throw an error from here and handle it on client
if (badThing) {
throw new Meteor.Error('bad-thing', 'A bad thing happened.');
}
}
});
Hope this helps. This is all off the top of my head with no testing at all. But it should give you a better idea of an improved structure when it comes to database operations.
Addressing your edits
Your code looks good, except a couple issues:
You're defining Posts twice, don't do that. Make a file, for example, /lib/collections/posts.js and put the declaration and instantiation of Mongo.Collection in there. Then it will be executed on both client and server.
Your console.log would probably return an error, or zero, because Posts.insert is asynchronous on the client side. Try the below instead:
.
Posts.insert({title: "title-1"}, function (error, result) {
console.log(Posts.find().count());
});
I've been looking on forums for 2 days now and can't find a good answer so I'll just post it.
I appear to be having a problem posting JSON back to the controller to save. The JSON should map to model view but it keeps getting default(constructor)values rather then the values from the POST.
We have a series of JS widgets that contain a data field with json in them. We do all our data manipulation in these widget objects on the client side. When a user wants to save we grab the data we need from the widgets involved and we put it into another JSON object that matches a ViewModel and POST that back to the server.
For example:
$("#Save").click(function () {
if (itemDetails.preparedForSubmit() && itemConnections.preparedForSubmit()) {
itemComposite.data.Details = itemDetails.data;
itemComposite.data.Connections= itemConnections.data;
$.post(MYURL, itemComposite.data);
} else {
alert("failed to save");
}
});
The preparedForSubmit() method simple does stuff like any validation checks or last minute formatting you might need to do client side.
The itemDetails widgets data matches a ViewModel.
The itemConnections widgets data matches a collection of ViewModels.
The Controller looks like this:
[HttpPost]
virtual public JsonResult SaveItemDetailsComposite(ItemComposite inItemData)
{
if (ModelState.IsValid)
{
try
{
_Mapper.Save(itemComposite.Details , itemComposite.Connections);
return Json(true);
}
catch (Exception ex)
{
_log.Error("Exception " + ex.InnerException.Message);
throw;
}
}
return Json(SiteMasterUtilities.CreateValidationErrorResponse(ModelState));
}
The ItemComposite Class is a simple View Model that contains a single itemDetails object and a collection of itemConnections. When it returns data to here it is just getting the default data as if it got a new ItemComposite rather than converting the POST data.
in Firebug I see the data is posted. Although it looks weird not automatically formatted in firebug.
Are you saying that itemComposite.data is formatted as a JSON object? If so, I'm pretty sure you're going to have to de-serialize it before you can cast it to your object. Something like:
ItemComposite ic = jsSerializer.Deserialize<ItemComposite>(this.HttpContext.Request.Params[0]);
You may want to look into a framework like JSON.NET to ensure that your data is being serialized properly when it gets supplied to your Action.
JSON.NET seems like it's one of the main stream frameworks: http://json.codeplex.com/releases/view/43775
Hope this helps.
Cory
You could also use the JSON Serializer in WCF: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.json.datacontractjsonserializer.aspx
SO wouldn't let me put both links in one answer, sorry for the split answer.
Thanks everyone. I think I have solved my problem and I'm pretty sure that I had four issues. For the most part I followed thatSteveguys's suggestion and read more on this article: http://haacked.com/archive/2010/04/15/sending-json-to-an-asp-net-mvc-action-method-argument.aspx
Using jQuery's post() method and specifying json as the type didn't seem to actually send it as json. By using the ajax() method and specifying json it sent it as json.
The JSON.serialize() method was also need to cleanly send over the json.
Also my ViewModel design was a big problem. We are using the MS code analytic build junk and it didn't want me having a setter for my collections in the ViewModel. So me being from a java/hibernate world, thought it didn't need them to bind and it would just come in as a serialized object magically. Once I just suppressed the error and reset up my setters. I am getting the collections now in my controller.
I believe using the MVC2 Future's Value Providers are doing something but it still doesn't convert json dates robustly, So I am still investigating the best way to do that.
I hope my issues help out others.
UPDATE: using this method to update collections of data appears to be super slow. A collection with 200 entries in it and 8 fields per entry takes 3 minutes to get to the controller. Just 1 or 2 entries take very little time. The only thing I know of that is happening between here is data binding to the model view. I don't know if MVC2 provides a easy way to send this much data and bind it.