What does template.data._id do in Meteor? - javascript

Beginner Meteor/JS question:
When associating objects in Meteor I see small line of code that I'm not understanding. For example, post with associated comments.
var $body = $(e.target).find('[name=body]');
var comment = {
body: $body.val(),
postId: template.data._id
};
So get the content of the comment, put it in the variable "comment", and also create a postId to go into this comment so you know what post the comment belongs to. This postId is being called in with *'template.data._id'*
My questions are:
So you call template, then wouldn't you want to call the template name? Not data? Where is data coming from?
That aside, so you call data...and then ._id, are there other options to 'data'? IE
template.data.(option)
This isn't working for me, haha, *console.log(template.data._id);* is coming back undefined. So it's not grabbing the object ID as advertised. I'm sure I messed something up.
Here's the surrounding code if you need more context:
https://github.com/DiscoverMeteor/Microscope/blob/master/client/views/comments/comment_submit.js

Beginning with the easiest first, under your point 3 it should read:
console.log(template.data._id);
As to points 1 and 2, the key idea to note is that your code is being called inside of Template.commentSubmit.events({}). Inside of this object (the "{}"), you are working with an instance of the template in the document, including whatever data is being passed to that instance. In a different template, you will receive different data and hence template.data will consist of different keys and values. You can check out more in the documentation here, http://docs.meteor.com/#template_inst.
So in answer to your question, the reason you can invoke "data" rather than the template name is that the template name has already been provided by Template.commentSubmit. Note that inside of events({}), "this" will generally be equivalent to "template.data." So
console.log(template.data._id);
will generally be equivalent to
console.log(this._id);
The properties you can access on the data will always vary based on the instance received by the template. For example, if template.data consists of {_id: 1, name: "x", location: "y"}, you can retrieve these values by calling the keys, e.g. "template.data.name" or "template.data._id" etc. In your example, you are correct that you are setting postID to template.data._id.
The data arriving to the template comes from your Meteor.subscriptions. Hope this helps.

The template has a data context. When you use an {{#each}} block you are iterating through data, in this case posts.
So using template.data retrieves the data context for the template. It would refer to a post from where the comment form is. So template.data._id is the equivalent of this post._id (where post is the post you're commenting on).
The thing is I think this was removed from meteor. It was there a couple of versions back. I'm not sure about this but that's what I thought. I would have thought the correct code should be this._id. Where this ends up being the data context of the form (which would again be the post).
Could you check if that gives you undefined if you changed it out?

The .events method of a template takes in an event map:
http://docs.meteor.com/#eventmaps
The callbacks for each of the events (in this case 'submit form') can take two arguments. The first one 'e' is the javascript event object and the second one 'template' is the instance of the template where the event occurred. That template instance has a bunch of utility methods/properties (see http://docs.meteor.com/#template_inst) one of which is .data (see http://docs.meteor.com/#template_data). That .data property returns the data that the template was bound to (in your Microscope example it looks like it's bound to a comment object) and that object (in this case) has an _id property.
Note that the 'template' (little T) is not the same as Template (big T).

Related

Why is my console.log in LWC showing variable data in proxy handler

I'm trying to console.log a variable's value but on the browser console instead of printing the variable (an object in my case), I am getting a Proxy container with format like
Proxy {}[[Handler]]: En[[Target]]: Array(0)[[IsRevoked]]: false
On opening the [[Handler]], I get some inner properties which contains an originalTarget property.
On expanding the originalTarget , my data is shown.
How do I get this data to show properly in console and also access it in my LWC ?
this.variableName returns value in a Proxy
If you want to view proxy data so use this :
JSON.stringify(this.caseList)
And further if you want to use it in your lwc use this:
let cases = JSON.parse(JSON.stringify(this.caseList))
console.log(cases);
I hope you find the above solution helpful. If it does, please mark it as Best Answer to help others too.
Reason : whenever we mark any Javascript object as #track, Salesforce wraps the object inside creating proxy objects.
Solution: #Rajat Jaiswal answer.

Object.assign not working as expected

I have one object called bookings, and inside it I have several properties, and i want extend with Object.assign, like this:
let data = Object.assign(booking, {
hiw: event.hiw[booking.locale],
tip: event.tip[booking.locale],
start: moment(event.start).format('L')
});
But when I print the data, the result will be the same object from the source (booking), so hiw, tip and start will be ignored, but... if I try to do:
let data = Object.assign({}, {
hiw: event.hiw[booking.locale],
tip: event.tip[booking.locale],
start: moment(event.start).format('L')
});
This will work perfect.
My question is: what am I doing wrong here? Why can't I extend booking and I can extend the empty object?
That's definitely not a problem with async code, when i try to extend booking, he already exist with all properties inside.
I also was trying to use underscore extend method, and the behavior is exactly the same.
Mongoose documents (model instances) are special: they will only print properties that are present in the schema.
If properties aren't defined there, they won't show up when you console.log() them (and also not if you convert the document to a plain JS object with obj.toObject()).
This means that using Object.assign() will only work if you assign properties that are also present in the schema. Any properties that aren't declared will not be shown (nor saved to the database).
If your intention is to use the document for output, you should convert it to a proper JS object first before assigning to it:
let data = Object.assign(booking.toObject(), {
hiw : event.hiw[booking.locale],
tip : event.tip[booking.locale],
start : moment(event.start).format('L')
});

Ember computed property doesn't fire when bound to deeply nested property

This JSBin isolates a problem I ran into in my code. I have a hierarchy of embedded models and a computed property (data) that is supposed to fire whenever a value at the very bottom of the chain changes (symbol). The example displays the property directly as well as the result of the computed property. A button changes the value on click. You'll see that it updates the property but the computed property doesn't fire. Why doesn't selectedAllocation.positions.#each.instrument.symbol work to trigger the computation when any instrument.symbol changes?
If the example seems contrived, it's only because I tried to abstract something that is more complex in reality, e.g. there is more than just one object in these arrays and data is necessary because another library expects a simple JS object in a particular format.
Note that #each only works one level deep. You cannot use nested forms
like todos.#each.owner.name or todos.#each.owner.#each.name.
http://emberjs.com/guides/object-model/computed-properties-and-aggregate-data/
You'll need to create an alias to bring symbol up one level (Not a coffeescript guy, hopefully you can read through the hacking, the positions alias below is for kicks and giggles, makes no difference).
App.Position = Ember.Object.extend({
instrumentSymbol: Em.computed.alias('instrument.symbol')
})
App.IndexController = Ember.ArrayController.extend
selectedAllocation: null
positions: Em.computed.alias('selectedAllocation.positions'),
data: (->
#get("positions").map (position) ->
symbol: position.get "instrumentSymbol"
).property "positions.#each.instrumentSymbol"
...
http://jsbin.com/bivoyako/1/edit

Knockout mapping plugin [create, update]: objects created, cannot update

I've posted my code here: http://jsfiddle.net/HYDU6/6/
It's a pretty stripped-down version of what I'm actually working with, but captures the essence of my problem. My view model is like so:
var viewModel = {
objects: {
foo: [
{ text: "Foo's initial" },
],
bar: [
{ text: "Bar's initial" },
]
}
}
I'm using the ko.mapping plugin and my create handler for objects instantiates Obj from objects.foo and then objects.bar, returning the resulting two items in an array. This part works fine; I use
var view = {};
ko.mapping.fromJS(viewModel, mapping, view);
My issue is updating based on new data. (i.e., getting data from the server). I have an object of new data and I attempt
ko.mapping.fromJS(new_model, mapping, view);
I suspect this is incorrect but I have not been able to get it working despite extensive searching. (Trust me, it's been days. ): Anyway, thanks for any help.
EDIT: So I've mostly figured it out - I was depending too heavily on mapping.fromJS and certain things were not being wrapped into observables. I also realized that I didn't need the create(), only the update(), as it is called after create() anyway. If you have a similar problem let me know!
John,
When updating your data using ko.mapping be sure you don't create a new item. Your UI is already bound to the existing items, so you just want to update the values of the existing item properties; not create new ones. For the example you posted, you'll want to adjust your "update" method of your map to insert the new values into the correct ko.observable property, rather than creating a new object in it's place. The ko.mapping "update" method has a few different parameter lists depending on usage, with the third parameter being the target object of the map. You would want to update that object's properties.
obj.target[label].items[0].text(obj.data[label][0].text);
But, that's a bit of a mess. You'll probably want to create a second level of mappings (create / update) to handle "deep" object hierarchies like in your fiddle. For example one map for objects at the "foo/bar" level, and another call to ko.fromJS from within "update" with another map for the child Obj() objects.
After fixing that, you'll run into a couple simple binding errors that you can fix using another "with" binding, or a "foreach" binding for the child arrays.
Overall, you've just run into a couple common pitfalls, but nothing too severe. You can learn a bit more about a few of these pitfalls on my blog here : http://ryanrahlf.com/getting-started-with-knockout-js-3-things-to-know-on-day-one/
I hope this helps!

List of autocomplete items in Dojo FilteringSelect

I have a typical FilteringSelect that works when I type in it and I can retrieve the value from the box, which is good. However, I'm trying to determine if I can access to that list of items that it returns.
For instance, if I have the following items in my store:
apple
axe
bananna
And I type in 'a' I want to get the an array that gets me 'apple' and 'axe'.
I'm assuming this will go somewhere in the onKeyPress: function , I'm just not familiar enough with the documentation. I've looked into dijit.byId('selectId') but from there I just don't know the API/documentation well enough
You should look at the API Documentation. There you can see a summary of all events possible. When you read it, you should come to the event called onSearch which returns 3 parameters:
the query
the results
some options
So what you want is the onSearch event and read the results parameter. I made a JSFiddle to show you an example.
Another possible solution is to query the store directly, which can be useful if you don't need the FilteringSelect, but if you just want to get a list of items based on a query. You can also see how that works in my JSFiddle.
EDIT: I Just noticed that you can't access the API documentation. You should really try another browser then, since the API documentation contains a lot of interesting things and is usually the reference for events/methods and properties.
I would have a look at the following properties from the Dojo API page:
https://dojotoolkit.org/api/ (click on dijit/form/FilteringSelect)
query
Defined by dijit/form/_SearchMixin
A query that can be passed to store to initially filter the items. ComboBox overwrites any reference to the searchAttr and sets it to the queryExpr with the user's input substituted.
queryExpr
Defined by dijit/form/_SearchMixin
This specifies what query is sent to the data store, based on what the user has typed. Changing this expression will modify whether the results are only exact matches, a "starting with" match, etc. dojo.data query expression pattern. ${0} will be substituted for the user text. * is used for wildcards. ${0}* means "starts with", ${0} means "contains", ${0} means "is"
searchAttr
Defined by dijit/form/_SearchMixin
Search for items in the data store where this attribute (in the item) matches what the user typed
For example (Haven't tried this so not sure if it will work or not):
* Use attr to retrieve or set dojo properties.
var srchItems = dijit.byId('resistForm').attr("searchAttr","a");

Categories

Resources