Why Is Spacebars Template Not Being Completely Rendered? - templates

I've got a strange situation where my template is not rendering every field
In this template everything within the #each renders correctly.
The first {{photographer}} in the h2 is blank, however. Same template. Any ideas?
This happens the same way with a different template too.
this can be seen at http://photo-story.meteor.com/photog/Dean
full code at https://github.com/deanwenick/cf07-stories
Thanks a lot
'<template name="photog">
<h2>These are {{photographer}}'s stories</h2>
{{#each photog}}
<img src="{{photos.[0]}}" alt="great image from a story">
<div class="caption">
<h3>{{storyName}}</h3>
<p>Photographer: {{photographer}}</p>
<p>Editor: {{editor}}</p>
</div>
{{/each}}
</template>'

The photographer's name isn't available outside of the {{#each}} section of the template because the data returned from your photog route's query has no top-level photographer field:
{
"photographer" : "Dean",
"editor" : "Dean",
"votes" : 0,
"photos" : [
"/pics/wenick_20131110_171.jpg",
"/pics/wenick_20131110_182.jpg"
],
"storyName" : "D2BS West 2013",
"_id" : "GCYR9MnYrrvxRSBQB"
}
{
"photographer" : "Dean",
"editor" : "Dean",
"votes" : 1,
"photos" : [
"/pics/wenick_20130409_149.jpg",
"/pics/wenick_20130409_158.jpg"
],
"storyName" : "Bill's Party",
"_id" : "tCrFAm7X6vFSbiadC"
}
You can add another returned data value in the photog route (in client/helpers/router.js) so that it looks like this:
return {
photog: Stories.find( {photographer: this.params.photographer} ),
photographer: this.params.photographer
};
...and then ensure that the line referencing the photographer's name (in client/views/stories/photog.html) is this:
<h2>These are {{photographer}}'s stories</h2>

The whole template is called on an object that is not shown. Lets all call this object top. I assume it looks like this:
{
"photog": [
{
"photographer": "name",
"editor": "name",
"photos": []
},
{
"photographer": "name2",
"editor": "name2",
"photos": []
}
]
}
The {{#each}} loops through the called photog array. Inside the each the variable scope is changed so that you can directly access {{photographer}}.
But outside of the each, which {{photographer}} should the template reference? It doesn't know. One solution would be to hard wire this with a template helper. Add something like this to your javascript:
Template.photog.photographer = function()
{
return "the name";
}

Related

how to obtain data argument to provide to mustache template in moodle

In moodle (4.0) I have the need to call
$this->output->render_from_template('core_courseformat/local/content/section/cmlist', $sectionData);
From within a renderer. The goal is to render the normal, native, cmlist component in a particular place on the page. But the way that I am currently getting the value of $section does not seem to work. My template renders nothing. I can see from the github source that this template expects data in this format:
Example context (json):
{
"cms": [
{
"cmitem": {
"cmformat": {
"cmname": "<a class=\"aalink\" href=\"#\"><span class=\"instancename\">Forum example</span></a>",
"hasname": "true"
},
"id": 3,
"module": "forum",
"extraclasses": "newmessages"
}
},
{
"cmitem": {
"cmformat": {
"cmname": "<a class=\"aalink\" href=\"#\"><span class=\"instancename\">Assign example</span></a>",
"hasname": "true"
},
"id": 4,
"module": "assign",
"extraclasses": ""
}
}
],
"hascms": true,
"showmovehere": true,
"movingstr": "Moving this activity: folder example",
"cancelcopyurl": "#",
"movetosectionurl": "#",
"strmovefull": "Move 'folder example' to this location"
}
}}
https://github.com/moodle/moodle/blob/1d99ba19a21d57e9f1ed4211a8eeee00e50b7baf/course/format/templates/local/content/section/cmlist.mustache
But here's the challenge. How do I get an object in that format with the data needed to feed the template so it can render the correct CM list items?
Currently I am tring:
$sectionData = get_fast_modinfo($course->id)->get_section_info($section);
But it doesn't seem to return the data structured in the right way.
Any help appreciated.
You can use the function export_for_template of cmlist render class.
Something like this:
$cmlist = new \core_courseformat\output\local\content\section($format, $section);
$data->cmlist = $cmlist->export_for_template($OUTPUT);
and then send the data to the template.
I recommend you imitate the behavior of a moodle as he performs here:
https://github.com/moodle/moodle/blob/7ce003b666a66b465ce9335f430a6e4d3535a7f1/course/format/classes/output/local/content/section.php#L223

Why does geocoding let me choose an exact address and then once clicked show different city name?

This question concerns a variation on the Try It Yourself section on the Google Maps Place Autocomplete Address Sample, from which you can get a Fiddle with a demo API key.
The problem is I thought I had it working perfectly just now after spending two hours trying to get it to work the way I want. When I typed an address where I used to live, the autosuggest suggested my address, 82 hopkins rd, northfield, ct, then when I click the result it fills in my town as being Litchfield, CT, which is actually the larger town one town over, and also the same name of the County that Northfield is in. Google obviously knows about Northfield though it is a tiny town, because it shows the exact address I want but when I click it why would it show Litchfield? If I try some other towns, etc. I don't see this happening. I imagine there is no solution because when I tried to change:
{types: ['geocode']}); to {types: ['(cities)']}); and tried typing in Northfield, then it showed other states with the town Northfield but not Connecticut. So I guess my old town is too small for Google to care about but then why show it in the autosuggest? Strange...
// This example displays an address form, using the autocomplete feature
// of the Google Places API to help users fill in the information.
// This example requires the Places library. Include the libraries=places
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
var placeSearch, autocomplete;
var componentForm = {
locality: 'short_name',
administrative_area_level_1: 'short_name',
};
function initAutocomplete() {
// Create the autocomplete object, restricting the search to geographical
// location types.
autocomplete = new google.maps.places.Autocomplete(
/** #type {!HTMLInputElement} */(document.getElementById('autocomplete')),
{types: ['geocode']});
// When the user selects an address from the dropdown, populate the address
// fields in the form.
autocomplete.addListener('place_changed', fillInAddress);
}
function fillInAddress() {
// Get the place details from the autocomplete object.
var place = autocomplete.getPlace();
for (var component in componentForm) {
document.getElementById(component).value = '';
document.getElementById(component).disabled = false;
}
// Get each component of the address from the place details
// and fill the corresponding field on the form.
for (var i = 0; i < place.address_components.length; i++) {
var addressType = place.address_components[i].types[0];
if (componentForm[addressType]) {
var val = place.address_components[i][componentForm[addressType]];
document.getElementById(addressType).value = val;
}
}lat = place.geometry.location.lat();
lng = place.geometry.location.lng();
document.getElementById('lat').value = lat;
document.getElementById('lon').value = lng;
}
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle({
center: geolocation,
radius: position.coords.accuracy
});
autocomplete.setBounds(circle.getBounds());
});
}
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=NOTMYREALKEYHERE&libraries=places&callback=initAutocomplete"
async defer></script>
You're seeing these results because Northfield is not a city. According to Wikipedia, Northfield, CT is "an unincorporated village in the town of Litchfield, Litchfield County, Connecticut". However, you're in luck: The example that you're viewing is using the API output address_component, where formatted_address for your place is correctly listed as 82 Hopkins Rd, Northfield, CT 06778, USA.
From the "Geocoding Address Types section" in the Google Maps Javascript API documentation, there are a wide variety of types of geocoding result. Some, like locality, require incoporation; sublocality_level_1 through sublocality_level_5 refer to areas narrower than that.
Even without your Javascript, you can do a search:
https://maps.googleapis.com/maps/api/place/findplacefromtext/json?key=YOUR_KEY&inputtype=textquery&input=82+hopkins+rd,+northfield,+ct
...to get a place ID of ChIJZYpAVT2W54kRPnm9uVA3cpc, and then you can do a subsequent place search:
https://maps.googleapis.com/maps/api/place/details/json?key=YOUR_KEY&place_id=ChIJZYpAVT2W54kRPnm9uVA3cpc
...to reveal the different types of address component:
"address_components" : [
{
"long_name" : "82",
"short_name" : "82",
"types" : [ "street_number" ]
},
{
"long_name" : "Hopkins Road",
"short_name" : "Hopkins Rd",
"types" : [ "route" ]
},
{
"long_name" : "Northfield",
"short_name" : "Northfield",
"types" : [ "neighborhood", "political" ]
},
{
"long_name" : "Litchfield",
"short_name" : "Litchfield",
"types" : [ "locality", "political" ]
},
{
"long_name" : "Litchfield",
"short_name" : "Litchfield",
"types" : [ "administrative_area_level_3", "political" ]
},
{
"long_name" : "Litchfield County",
"short_name" : "Litchfield County",
"types" : [ "administrative_area_level_2", "political" ]
},
{
"long_name" : "Connecticut",
"short_name" : "CT",
"types" : [ "administrative_area_level_1", "political" ]
},
{
"long_name" : "United States",
"short_name" : "US",
"types" : [ "country", "political" ]
},
{
"long_name" : "06778",
"short_name" : "06778",
"types" : [ "postal_code" ]
}
To your point, this also gives a formatted_address of 82 Hopkins Rd, Northfield, CT 06778, USA, which is what you're looking for.
In the example you quoted, which is the "try it yourself" section on the Place Autocomplete Address Form sample, there are two separate processes happening: The Autocomplete searches for your address and suggests a formatted address, and then the place details call unpacks the structured pieces of the address as needed. However, as in the code comments:
<!-- Note: Selection of address components in this example is typical.
You may need to adjust it for the locations relevant to your app. See
https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete-addressform
-->
Thus, by tweaking the form as I have in this Fiddle, you can see that your formatted address includes Northfield, listed as a Neighborhood, even though the city is properly Litchfield. Note that this is also location-specific behavior, as "1600 Pennsylvania Avenue, Washington DC" returns a Neighborhood of "Northwest Washington" is omitted from the formatted address.
Thus, to get the behavior you want, just use the formatted_address, or consider expanding the address_components to provide the granularity you need.

Google Polymer: google-map-search doesn't work

<paper-dialog id="post" entry-animation="scale-up-animation" exit-animation="fade-out-animation">
<div class="find-area">
<paper-textarea on-input="find" id="find_textarea" class="find-place-text" label="Find your place" maxlength="250"></paper-textarea>
</div>
<div class="map-area">
<google-map id="[[map]]"
api-key="000000000myapi000000"
latitude="[[lat]]"
longitude="[[lon]]"
fit-to-markers>
</google-map>
<google-map-search id="google_search"
globalSearch="true"
map="[[map]]"
results="[[results]]">
</google-map-search>
</div>
<paper-button on-tap="[[upload]]">Accept</paper-button>
<label>coords:[[ results::lat ]], [[ results::lon ]]</label>
<label>query:[[ query ]]</label>
<label>map:[[ map ]]</label>
<label>results:[[results]]</label>
</paper-dialog>
<script>
function _showPosition(position) {
try {
x.latitude = position.coords.latitude;
x.longitude = position.coords.longitude;
}catch (err){
alert(err+'; position:'+position)
}
}
function showError(error) {
alert('error:'+ error)
}*/
function _submit(event) {
Polymer.dom(event).localTarget.parentElement.submit();
}
Polymer({
is: 'profile-new-post',
properties: {
enable : {
type: Boolean,
value: true
},
lat : {
value : 37.77493
},
lon : {
value : -122.41942
},
query : {
type : String,
value : ""
},
results : {
type : Array
},
map : {
type : Object
}
},
func : function (e) {
this.map = this.$.map;
post.open();
},
find : function (e) {
this.$.google_search.query = this.$.find_textarea.value;
this.query = this.$.google_search.query;
this.$.google_search.search();
this.lat = this.$.google_search.results.latitude;
this.lon = this.$.google_search.results.longitude;
//alert(this.$.google_search.results.latitude + '; ' + this.$.google_search.results.longitude)
},
I'm trying to use [[]] brackets because of django use {{}}. Map, results and coords are empty at output lables. It shows map with San Francisco but when i try to print text in input it doesn't want to search. The aren't any errors in console. I've saw tutorial video from google about this, but there was old version of Polymer and many things like {{ $.element.atribute }} inside element head doesn't work (it doesn't know what '$' is). Maybe someone can explain for me what's the biggest difference between [[ ]] and {{ }}, because i can't understand it from official tutorial?
Solve: to solve it, i must put source from inside dialog to new template with property is="dom-bind.
<p><paper-button raisedButton on-tap="upload">Upload</paper-button></p>
<paper-button id="dialogbutton" on-tap="func">Post</paper-button>
<paper-dialog id="post" entry-animation="scale-up-animation" exit-animation="fade-out-animation">
<template is="dom-bind">
<div class="find-area">
<paper-input value="{{ input_query }}" on-input="find" id="find_textarea" class="find-place-text" label="Find your place" maxlength="250"></paper-input>
</div>
<div class="map-area">
<google-map-search
id="google_search"
map="{{ map }}"
query="{{ input_query }}"
results="{{results}}"
on-google-map-search-results="searchingComplite">
</google-map-search>
<google-map
map="{{map}}"
latitude="{{results[0}.latitude}}"
longitude="{{results[0}.longitude}}">
</google-map>
</div>
<paper-button on-tap="upload">Accept</paper-button>
<label>coords:{{ lat }}, {{ lon }}</label>
<label>query:{{ query }}</label>
<label>map:{{ map }}</label>
<label>results:{{ results }}</label>
</template>
There are a couple of issues here:
Yes, the [[]] brackets are the problem here because they enforce one-way binding. That means that the results from the google-map-search can't propagate upwards and the labels are empty. You need to change the results=[[results]] to results={{results}} to enable two-way binding
For declerative event handlers, you don't need any brackets. So this line <paper-button on-tap="[[upload]]">Accept</paper-button> should be ?<paper-button on-tap="upload">Accept</paper-button>
To access sub-properties of an data bound object you need to use dot notation (.). This line <label>coords:[[ results::lat ]], [[ results::lon ]]</label> should be changed to <label>coords:[[ results.lat ]], [[ results.lon ]]</label>
I would also change lat and lon to computed properties which either return default values (alternatively just use attributes on your google-map element for that) or the values from your search result.

Is there a versatile engine to translate JavaScript object into a different object?

I'm looking for a versatile way to translate a big object into a custom object using a template. It should be able to traverse each value of arrays, and reference parent values in the loop.
Imagine an object like:
{
collectionId : 432,
products : [
{
productId : 1155,
suppliers : [
{
supplierId : 252,
supplier : 'SupplyCompany',
shipments : [
{
date : 'foo',
order : 'bar',
id : 45
},
{},
{},
//...
]
},
{},
{},
//...
]
},
{},
{},
//...
],
}
I would for example want to flatten this into:
[
{
collectionId : 432,
productId : 1155,
supplierId : 252,
supplier : 'SupplyCompany',
date : 'foo',
order : 'bar',
id : 45
},
{},
{},
//...
]
Right now I'm using manual for for for loops, but this isn't very versatile when the source object changes or one of my output objects change. It's not very maintainable to 'hardcode' these translations.
There are templating engines that do something resembling what I want, e.g. JsRender. It theoretically allows you to do what I need:
{{for products}}
{{for suppliers}}
{{for shipments}}
collectionId : {{:#parent.parent.parent.parent.data.collectionId}}
productId : {{:#parent.parent.parent.data.productId}},
supplierId : {{:#parent.parent.data.supplierId}},
supplier : {{:#parent.parent.data.supplier}},
date : {{:date}},
order : {{:order}},
id : {{:id}
{{/for}}
{{/for}}
{{/for}}
(This is pretty nice because Mustache doesn't allow you to do this.)
However, jsRender reads strings (templates) and output strings (html). The overhead from all the stringify and parse is not doing any good in my case.
I'm looking for something that does this kind of processing with objects. From objects to objects.
There are a few ways you can do that kind of thing using JsRender. For example it is really easy to create custom tags, converters, etc. (or use helper functions) and make them have side effects of mapping objects to some output object. Of course the philosophy of JsRender is generally to be declarative, without side effects, but if you want to you can indeed use it a 'processor'.
Here is a jsfiddle sample which can give you some ideas:
It uses a custom tag:
$.views.tags({
addObject: function(target) {
var item = $.extend({}, this.tagCtx.props);
target.push(item);
...
}
})
and a template:
{{for products ~collectionId=collectionId}}
{{for suppliers ~productId=productId}}
{{for shipments ~supplierId=supplierId ~supplier=supplier}}
{{addObject ~target collectionId=~collectionId productId=~productId supplierId=~supplierId supplier=~supplier date=date order=order id=id/}}
{{/for}}
{{/for}}
{{/for}}

Embedded Records in the latest ember-data #1.0.0

Firstly, I will be using ember-cli for development and I get JSON from the server in embedded format:
Single Objects
{
"id" : 1,
"name" : "Tom",
"surname" : "Smith",
"address" : {
"id" : 2,
"street" : "23",
"city" : "...",
...
}
}
Arrays
[
{
"id" : 1,
"name" : "Tom",
"surname" : "Smith",
"address" : {
"id" : 2,
"street" : "23",
"city" : "...",
...
}
},
{
"id" : 2,
"name" : "Tom",
"surname" : "Smith",
"address" : {
"id" : 4,
"street" : "23",
"city" : "...",
...
}
},
...
]
I have worked out how to append the prefix onto each payload using the following in RestAdapter.
export default DS.RESTSerializer.extend({
extract : function(store, type, payload, id, requestType) {
var typeKey = type.typeKey,
typeKeyPlural = typeKey.pluralize();
//Add name of model to payload as expected by ember data
var modelName = Array.isArray(payload) ? typeKeyPlural : typeKey;
var normalizedPayload = {};
normalizedPayload[modelName] = payload;
return this._super(store, type, normalizedPayload, id, requestType);
},
)}
I have searched around all over the place and can see all these different ways of embedding records in ember.
The official docs say to us the DS.EmbeddedRecordsMixin Class. But this would mean I would need to create a DS.ActiveModelSerializer for every single model, I would rather define the attribute in the model itself {embedded : "always"}.
http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html
This actually sort of worked but is obviously old because the parameters have since changed. It is a bit of a hack in my opinion.
http://mozmonkey.com/2013/12/serializing-embedded-relationships-ember-data-beta/
There is this project, but no docs or example of how to get it working.
https://github.com/pixelhandler/ember-data-extensions
All in all I am very confused.
The correct approach is to use a serializer and the DS.EmbeddedRecordsMixin.
You are really fighting against separation of concerns by specifying serializer options in your models.
So, that said, lets get down to the evil.
You can simply copy DS.EmbeddedRecordsMixininto your application and modify the way it checks its options to instead examine the relationship meta data on the model type. You can then extendyour default ApplicationSerializer with your custom mixin so that you don't have to specify a serializer for all your models.
You will need to modify the following function, from this:
attrsOption: function(attr) {
var attrs = this.get('attrs');
return attrs && (attrs[camelize(attr)] || attrs[attr]);
}
To something like this (note the extra param):
attrsOption: function(attr, type) {
var meta = type.metaForProperty(attr) || type.metaForProperty(camelize(attr));
return meta && meta.options;
}
You will also need to modify all the callers of attrsOption to pass the model type down, but you will then have your very own embedded record mixin that gets its options from the model type.
Then when specifying your model relationships you can use the embedded record options as follows:
address: DS.belongsTo('address', { embedded: 'always' })