Issues chaining object rendered from express in handlebars template - javascript

I'm having trouble finding the solution for a simple issue I'm currently having when trying to chain an object rendered from express in my handlebars file.
The object I'm trying to chain looks like so:
"generalContentOfferOne": {
"subCopy": {
"en-us": "Test Copy",
"bahasa": "Bergabunglah dalam..."
}
}
In my handlebars file, {{distributorJSON.generalContentOfferOne.subCopy}} renders Object object, which it should.
I have a localization variable I'm also rendering to handlebars. It will either be en-us or bahasa based on route. Anyways, now that I have this localization value, I figured I could use bracket notation to render the dynamic value in the object above. For example:
{{ distributorJSON.generalContentOfferOne.subCopy[{{localization}}] }}
Also tried:
{{ distributorJSON.generalContentOfferOne.subCopy.{{localization}} }}
These aren't working.. I'm guessing handlebars has it's own specific way to chain dynamic values? Or at least I hope so. Thanks for your help!

I couldn't find a default solution for this. So I built a handlebars helper.
Helper:
exports.returnDynamicPropValue = function(object, localization){
return object[localization];
}
Handlebars template:
{{ returnDynamicPropValue distributorJSON.generalContentOfferOne.subCopy localization }}

Related

Pass an array as a parameter for view rendering in twig

I'm pretty new to Twig.js, and notice that it lacks some documentation. In fact, I could only find extremely basic usage information on their GitHub wiki.
My views rely on a bunch of variables. All views extend from layout.twig which includes navbar.twig. The last one takes a lot of parameters, like color, names and others I haven't implemented yet.
The problem with this is that I would need to pass lots of variables every time a view gets rendered. So I thought a solution would be to pass an array each time, instead of multiple fieds. My question comes there, as in how I'd interact with this array
My current and inefficient solution can be represented when displaying an error:
res.render('error', {title: appConfig.name, color: appConfig.color (...)});
It would be better if I could pass an array and then interact with it inside of my twig view.
I'm open to other solutions. Being able to access appConfig from inside the view would be one of them, although I don't think that is possible. If it this though, please tell me how! I'll thank you forever.
Thanks in advance.
Pass appConfig:
var appConfig = { name: 'Jeremy', color: 'green' };
res.render('home', appConfig );
Render it:
<div class="{{ color }}">Hello {{ name }}</div>
Twig can work just fine with nested objects too:
var navConfig = { color: 'salmon' }
res.render('home', { nav: navConfig, name: 'Arnold' })
<nav class="{{ nav.color }}">{{ name }}</nav>
You can simply parse appConfig to JSON Object using JSON.parse.
Note - JSON.parse can tie up the current thread because it is a synchronous method. So if you are planning to parse big JSON objects use a streaming json parser.

I have some questions about Sapper/Svelte

I just started using Sapper (https://sapper.svelte.technology) for the first time. I really like it so far. One of the things I need it to do is show a list of the components available in my application and show information about them. Ideally have a way to change the way the component looks based on dynamic bindings on the page.
I have a few questions about using the framework.
First, I'll provide a snippet of my code, and then a screenshot:
[slug].html
-----------
<:Head>
<title>{{info.title}}</title>
</:Head>
<Layout page="{{slug}}">
<h1>{{info.title}}</h1>
<div class="content">
<TopBar :organization_name />
<br>
<h3>Attributes</h3>
{{#each Object.keys(info.attributes) as attribute}}
<p>{{info.attributes[attribute].description}} <input type="text" on:keyup="updateComponent(this.value)" value="Org Name" /></p>
{{/each}}
</div>
</Layout>
<script>
import Layout from '../_components/components/Layout.html';
import TopBar from '../../_components/header/TopBar.html';
let COMPONENTS = require('../_config/components.json');
export default {
components: {
Layout, TopBar
},
methods: {
updateComponent(value) {
this.set({organization_name: value});
}
},
data() {
return {
organization_name: 'Org Name'
}
},
preload({ params, query }) {
params['info'] = COMPONENTS.components[params.slug];
return params;
}
};
</script>
Now my questions:
I notice I can't #each through my object. I have to loop through its keys. Would be nice if I could do something like this:
{{#each info.attributes as attribute }}
{{attribute.description}}
{{/each}}
Before Sapper, I would use Angular-translate module that could do translations on strings based on a given JSON file. Does anyone know if a Sapper/Svelte equivalent exists, or is that something I might need to come up with on my own?
I'm not used to doing imports. I'm more use to dependency injection in Angular which looks a bit cleaner (no paths). Is there some way I can create a COMPONENTS constant that could be used throughout my files, or will I need to import a JSON file in every occurence that I need access to its data?
As a follow-up to #3, I wonder if there is a way to better include files instead of having to rely on using ../.. to navigate through my folder structure? If I were to change the path of one of my files, my Terminal will complain and give errors which is nice, but still, I wonder if there is a better way to import my files.
I know there has got to be a better way to implement what I implemented in my example. Basically, you see an input box beside an attribute, and if I make changes there, I am calling an updateComponent function which then does a this.set() in the current scope to override the binding. This works, but I was wondering if there was some way to avoid the function. I figured it's possible that you can bind the value of the input and have it automatically update my <TopBar> component binding... maybe?
The preload method gives me access to params. What I want to know if there is some way for me to get access to params.slug without the preload function.
What would be really cool is to have some expert rewrite what I've done in the best possible way, possibly addressing some of my questions.
Svelte will only iterate over array-like objects, because it's not possible to guarantee consistent behaviour with objects — it throws up various edge cases that are best solved at an app level. You can do this sort of thing, just using standard JavaScript idioms:
{{#each Object.values(info.attributes) as attr}}
<p>{{attr.description}} ...</p>
{{/each}}
<!-- or, if you need the key as well -->
{{#each Object.entries(info.attributes) as [key, value]}}
<p>{{attr.description}} ...</p>
{{/each}}
Not aware of a direct angular-translate equivalent, but a straightforward i18n solution is to fetch some JSON in preload:
preload({ params, query }) {
return fetch(`/i18n/${locale}.json`)
.then(r => r.json())
.then(dict => {
return { dict };
});
}
Then, you can reference things like {{dict["hello"]}} in your template. A more sophisticated solution would only load the strings necessary for the current page, and would cache everything etc, but the basic idea is the same.
I guess you could do this:
// app/client.js (assuming Sapper >= 0.7)
import COMPONENTS from './config/components.json';
window.COMPONENTS = COMPONENTS;
// app/server.js
import COMPONENTS from './config/components.json';
global.COMPONENTS = COMPONENTS;
Importing isn't that bad though! It's good for a module's dependencies to be explicit.
You can use the resolve.modules field in your webpack configs: https://webpack.js.org/configuration/resolve/#resolve-modules
This would be a good place to use two-way binding:
{{#each Object.values(info.attributes) as attr}}
<p>{{attr.description}} <input bind:value=organization_name /></p>
{{/each}}
Yep, the params object is always available in your pages (not nested components, unless you pass the prop down, but all your top-level components like routes/whatever/[slug].html) — so you can reference it in templates as {{params.slug}}, or inside lifecycle hooks and methods as this.get('params').slug, whether or not a given component uses preload.

How to display a list of all names of templates?

In the client, I know you can use Template.[template name] to reference a specific template.
How could you get a list of all custom meteor templates included in your app that have been created by you (not meteor or included from a package)?
Building on what Richard said, you can check if the property on the Template object is a template like this:
var templates = [];
for(var key in Template){
if(Blaze.isTemplate(Template[key])){
templates.push( key );
}
}
console.log( templates );
You'd probably have to use a specific naming convention for identifying your own templates.
Just do Object.keys(Template). I attached a screenshot from the console.
Basically meteor creates a class called Template. We can iterate over all the keys in the template class.

EmberJS Helper to dynamically call components [duplicate]

Lets say I have an array of widget objects on my controller and each widget object has member variable that is assigned the name of a component class. How can I get my template to invoke that component?
//widgets[0].widget.componentClass="blog-post"
{{#each widget in widgets}}
{{widget.componentClass}}
{{/each}}
Obviously the above example just spits out a series of string versions of the widget component classes. This however does work (as long as you got everything set up right):
//widgets[0].widgets.viewClass="blogPost"
{{#each widget in widgets}}
{{view widget.viewClass}}
{{/each}
That was our previous implementation, but we weren't happy with it. We're currently using a custom {{renderWidget ...}} tag with a handlebars helper as described here: Calling Handlebars {{render}} with a variable name. The default render helper has a similar problem where it would not invoke a render on the contents of a variable name. I'd be willing to write a custom component handlebars helper but I can't even figure out where to start. Thanks.
I tried this and it seems to work, but its just a lot of guesswork on my part:
Ember.Handlebars.registerHelper('renderComponent', function(componentPath, options) {
var component = Ember.Handlebars.get(this, componentPath, options),
helper = Ember.Handlebars.resolveHelper(options.data.view.container, component);
helper.call(this, options);
});
and you use it the same way:
{{#each widget in widgets}}
{{renderComponent widget.componentClass widget=widget}}
{{/each}}
In Ember 1.11, the new component helper allows you to do this:
{{#each widget in widgets}}
{{component widget.componentClass}}
{{/each}}
As of today, Jan 19th, 2015, 1.11 is not a stable release but this feature is in the canary version.
Sounds like I've run into a lot of the same problems as you. All components are registered as top level helpers, which means you can do a similar method to the one you linked of creating a handlebars helper that does the lookup. Like this:
Ember.Handlebars.registerHelper('lookup', function(component, options) {
component = Ember.Handlebars.get(this, component, options);
Ember.Handlebars.helpers[component].call(this, options);
});
Then in your template:
{{#each widget in widgets}}
{{lookup widget.componentClass}}
{{/each}}
Here's a jsbin with a working example: http://jsbin.com/ucanam/2482/edit
Hope that helps!
-- edit --
For some reason, the new version of handlebars makes calling helpers from other helpers impossible. Instead you can lookup the template through the Ember.TEMPLATES global. I've updated the JSBin to use this method.
You can also get the template via options.data.view.templateForName(component), but it feels a bit more brittle than Ember.TEMPLATES.
-- edit 2 --
It's changed again. Ember.Handlebars.resolveHelper is now the correct way to do it. See #StrangeLooper's answer.
If you are using Ember CLI and Coffeescript here is a version for that. Create the following file in app/helpers/render-component.coffee:
renderComponent = (componentPath, options)->
helper = Ember.Handlebars.resolveHelper(options.data.view.container, componentPath)
helper.call this, options
`export { renderComponent }`
`export default Ember.Handlebars.makeBoundHelper(renderComponent)`
From there, you can call {{render-component "foo-bar"}} from a template.
Since the Ember ecosystem is ever changing, here is the version I tested it on:
Ember-CLI v0.0.43
Ember v1.7.0
Handlebars 1.3.0

How to access data inside if statement in Handlebars templates

To the handlebars (version 1.0.0-rc.3) template I am passing two variables , one is the json and the other one is the string containing the current language on site.
self.template = template({ data: self.model, lang:self.lang });
Then inside the template file I have the following structure:
{{#each data}}
//this is working
{{../lang}}
{{#if this.title}}
{{this.desc}}
//i've tried this
{{../lang}}
//and this
{{lang}}
{{/if}}
{{/each}}
...but I couldn't access the lang value inside the if statement. What am I doing wrong?
I know you already solved your issue with a workaround but registering a Helper for doing a native way is cumbersome.
The thing is that every Handlebars helper overwrites the context and nest the new one inside the parent one, so you have to go up uone step further, like a UNIX like directory.
So, to access lang inside an each->if you have to use:
{{ ../../lang }}
I've find a solution by creating a handlebars helper function:
Handlebars.registerHelper('language', function() {
return self.lang; });
Then in the template i could use {{language}}
where ever I want.

Categories

Resources