Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to define and pass data to an Assemble partial inline? #228

Closed
keeganstreet opened this issue Jul 11, 2013 · 31 comments
Closed

Comments

@keeganstreet
Copy link

In ERB, I can do it like this:

<%= partial("inputtext", :locals => {
    :id => "fistname",
    :label => "First name"
}) %>

I know I can pass data to an Assemble partial if it is already defined, e.g:

{{> inputtext predefinedData }} 

But can I define the data inline?

@jonschlinkert
Copy link
Member

Currently we're limited to valid Handlebars syntax, so that wouldn't work with partials. But you could easily create custom helpers to do what you're trying to accomplish. With helpers, you're really only limited by the JavaScript you write.

You can also use underscore templates (lodash) in YAML front-matter (or in separate JSON or YAML files), so you could almost do what you're suggesting. It's not quite the same as defining data inline, but for example, you could do something like this:

---
title: Home
list: <% _.forEach(people, function(name) { %><li><%= name %></li><% }); %>
people:
- Jon Schlinkert
- Brian Woodward
---
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>{{title}}</title>
  </head>
  <body>
    <section class="people">
      <ul>
        {{{list}}}
      </ul>
    </section>
  </body>
</html>

And of course, the data could be supplied from anywhere. I'm just using YAML front matter as the example.

You can also mix and match whatever helpers, lo-dash templates, partials, etc.

Here are some links to related docs:

Let me know if you aren't able to find a way to accomplish what you need. There is a good chance we'll be able to figure out something idiomatic.

@keeganstreet
Copy link
Author

Hi @jonschlinkert,

Thanks for the reply. It looks like Handlebars is not designed for the problem I'm trying to solve.

I could write a simple custom helper such as:

helpers.inputtext = function(id, label) {
    return '<div><label for="' + id + '">' + label + '</label><input type="text" id="' + id + '" /></div>';
};

Which I could then use with:

{{{inputtext "firstname" "First name"}}}

But I don't like this solution because it requires me to put my HTML for the component in a JavaScript file.

Do you know of another way to make templates for interface components in Grunt?

@keeganstreet
Copy link
Author

I have come up with another solution which I think does the job OK. I'm probably not using Handlebars the way it was intended though.

I have a normal partial set up like this:

<div>
    <label for="{{id}}">{{label}}</label>
    <input type="text" id={{id}} />
</div>

I have a block helper set up like this:

helpers.parseJSON = function(data, options) {
    return options.fn(JSON.parse(data));
};

Then I include my partial like this:

{{#parseJSON '{"id": "firstname", "label": "First name"}'}}
    {{> inputtext}}
{{/parseJSON}}

The helper parses my JSON and passes it into the partial.

@jonschlinkert
Copy link
Member

I don't like this solution because it requires me to put my HTML for the component in a JavaScript file.

It all depends on what you're trying to get accomplished and what the most pragmatic trade-off is. In all proposed solutions you're doing something that isn't quite idiomatic. E.g. is it better to have a little HTML in your JavaScript, or have a data model (and data) in your markup?

That's an interesting solution you came up with, you have me really curious how you're using this. Also, do you mind if I add that as a helper to our handlebars-helpers library?

thanks for the update

@keeganstreet
Copy link
Author

Hey Jon, yes of course you are welcome to use the helper in the handlebars-helpers library. I'd be honoured!

We will use this pattern to build interface components and enforce consistency where these components are used. For example, we might build an input text component to ensure that all input text fields in the website:

  • are contained inside a DIV with a classname of "input"
  • have the attributes "id", "name" and "type"
  • have an associated label, and the "for" attribute on the label matches the "id" attribute on the input
  • optionally have a red asteriks if the field is required
  • optionally have a tooltip

Instead of having to write out the same HTML over and over again, we can define it once in a partial and then include that partial inline whenever we need it. Typically Handlebars requires data to be defined separately from templates. But in this use case it is easier to define the options for the partial where we include the partial. These options aren't "data" that should be separated from the template; they are really just options for the template.

@jonschlinkert
Copy link
Member

Very clever, thank you for sharing this and providing insight on why you're using it. Sounds like we share some common goals. I saw some of the projects you've created, also very interesting. You have some great ideas, let me know if Assemble doesn't do something you need and we'll get it figured out.

@jfroom
Copy link

jfroom commented Sep 30, 2013

IMHO, this parseJSON inline magic is very useful!

@jfroom
Copy link

jfroom commented Jan 16, 2014

The context seems to become limited to that JSON object that gets passed in however - my root data scope is not available. Is there a variable like 'root' or something I can call in my partial to get back to accessing the root context? I have a 'settings' object that gets lost as soon as I use parseJSON. If I could reference it as 'parent.settings' or 'root.settings' that would be one work around. Hmm.

@jonschlinkert
Copy link
Member

I'll need to look at the helper again to see what's happening to the context, but this should be a relatively easy thing to fix. also related: assemble/assemble-handlebars#18

@julia-r
Copy link

julia-r commented May 5, 2014

I just had the same problem as jfroom described. The handler works but the root data is not available. I did you figure something out? Also I would like to use translations in that partial (using this https://github.com/assemble/assemble-middleware-i18n), but I get an error message that "The language parameter is not defined"

@koistya
Copy link

koistya commented Jul 19, 2014

How to implement this with assemble.io?

nav.hbs

<ul class="nav">
<li{{#if active "home"}} class="active"{{/if}}><a href="/">Home</a></li>
<li{{#if active "about"}} class="active"{{/if}}><a href="/about">About</a></li>
</ul>

index.hbs

...
<section class="page-home">
    {{> nav active: "home" }}
</section>
<section class="page-about">
    {{> nav active: "about" }}
</section>
...

@owzzz
Copy link

owzzz commented Jul 24, 2014

How about the ability to pass to a partial a reference to a json file that is in your data folder.

We have a scenario where we want to share a partial between a styleguide and our actual application with different data. However, we are unable to do so unless we use YAML which feels kind of dirty adding the data in the page. Some times there is a lot of data, so we don't want to have to scroll the page to get to the actual markup.

An Example

We have a template inside our application which includes a partial named navigation which in turn references app-navigation.json

app-template.hbs

{{> navigation app-navigation}}

but then we also want to use the same partial but with slightly different data in the styleguide. We'd want to do something like this:

styleguide-template.hbs

{{> navigation styleguide-navigation}}

Both partials would populate themselves with the different [context]-navigation.json which sits inside the data folder.

Is this currently possible?

@doowb
Copy link
Member

doowb commented Jul 24, 2014

@owzzz yes, that's possible. You can create another .json file called styleguide-navigation.json. Basically, with handlebars, you can pass any object into the partial. Since we load the data files and add them to the root of the context based on their filename, you can just pass those objects in.

@owzzz
Copy link

owzzz commented Jul 24, 2014

So the above way of including a specific json is correct?

{{> navigation styleguide-navigation}}

If not, how do you "pass any object into the partial"?

Could you give me an example of how I'd do this?

@doowb
Copy link
Member

doowb commented Jul 24, 2014

That's the correct way. styleguide-navigation is the object containing the data from styleguide-navigation.json file.

@owzzz
Copy link

owzzz commented Jul 24, 2014

Perfect, will try this out.

Thanks.

@owzzz
Copy link

owzzz commented Jul 28, 2014

This didn't seem to work for me, how would you reference the data inside the template?

I tried

<h1>{{navigation.title}}</h1>

but it did not output anything.

@doowb
Copy link
Member

doowb commented Jul 28, 2014

What's the data in the json file look like?

If styleguide-navigation contains an object like this:

{
  "title": "This is the title"
}

Then inside the navigation partial, you do:

<h1>{{title}}</h1>

@adriaanbalt
Copy link

This is the data structure:

{
    "array":[
        {
            "name":"hello"
        },
        {
            "name":"world"
        }
    ]
}

This has worked for me

{{> partial array }}

Now I am trying to do something similar, but with an array of data.

{{> partial array.0 }}

This returns the following error:

Warning: Parse error on line 10:
{{> partial array.0 }}        </div>
--------------------^
Expecting 'ID', got 'INTEGER' Use --force to continue.

When I change the data to:

{
    "array":[
        {
            "content":{
                "name":"hello"
            }
        },
        {
            "content":{
                "name":"world"
            }
        }
    ]
}

This will work:

{{> partial array.0.content }}

Is there any way to get around this without having to add a sub-key "content" to the data?

@adriaanbalt
Copy link

SOLVED

{{> partial array.[0] }}

Will allow the array data to be passed by index

So if you do not want to loop through the array with an {{#each array}}, you could do:

{{> partial array.[1] }}
{{> partial array.[2] }}
{{> partial array.[3] }}

etc...

@jonschlinkert
Copy link
Member

that's great! thanks for sharing the solution here!

@owzzz
Copy link

owzzz commented Jul 30, 2014

Out of interest, is it possible to have nested partials with each partial needing different data to populate them.

Layout

{{> pagePartial pageData}}

Page Partial

{{> componentPartial componentData}}

I cannot seem to get this to work, I'm wondering if it only works with one data context.

@jonschlinkert
Copy link
Member

Yes... but, it can be a little tricky because of how handlebars manages context. your best bet is to look for handlebars documentation on the @root variable and accessing parent context in nested templates. hope that helps

@owzzz
Copy link

owzzz commented Jul 30, 2014

Thanks, will investigate..

@tuxsudo
Copy link

tuxsudo commented Sep 30, 2014

Another useful function:

helpers.withHash = function(options){
    return options.fn(options.hash);
}

This allows passing has options:

{{#withHash id="firstname" label="First Name"}}
    {{> inputtext}}
{{/withHash}}

@jonschlinkert
Copy link
Member

@tuxsudo yeah that's nice and simple, thanks for sharing it!

@onokumus
Copy link
Contributor

assemble v0.1.6.2

partials: btn-basic.hbs

<button class="{{modifier}}">
    {{text}}
</button>

pages

{{> btn-basic modifier="btn btn-danger btn-lg" text="click me"}}

result

<button class="btn btn-danger btn-lg">
  click me
</button>

@jonschlinkert
Copy link
Member

@onokumus this is a good question for stackoverflow, fwiw it's more of a handlebars questions

@esr360
Copy link

esr360 commented Sep 4, 2016

@onokumus is it possible to set default options with this method? If so, how?

@onokumus
Copy link
Contributor

onokumus commented Sep 4, 2016

@esr360

<button class="btn {{#if modifier}}{{modifier}}{{else}}btn-default{{/if}}">
    {{text}}
</button>
{{> btn-basic text="button 1"}}
{{> btn-basic modifier="btn-danger btn-lg" text="button 2"}}

result

<button class="btn btn-default">
  button 1
</button>

<button class="btn btn-danger btn-lg">
  button 2
</button>

@esr360
Copy link

esr360 commented Sep 4, 2016

@onokumus thank you so much, great solution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests