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

Feature Request: Pass parameters to section / partial #261

Closed
ElliotChong opened this issue Nov 10, 2013 · 13 comments
Closed

Feature Request: Pass parameters to section / partial #261

ElliotChong opened this issue Nov 10, 2013 · 13 comments

Comments

@ElliotChong
Copy link

It would be great to be able to add parameters to a section or partial similar to Dust.js's implementation.

Data:

{
   "name": "root",
   "anotherName": "root2",
   "A":{
      "name":"Albert",
      "B":{
         "name":"Bob"
      }
   }
}

Setting Strings
Template:

  {{#A.B foo="Hi" bar="good to see you"}}
      {{foo}} {{name}} {{bar}}
  {{/A.B}}

Output:

Hi Bob good to see you

Other Previously Defined Values
Template:

  {{#A.B foo=A.name bar=anotherName}}
      {{foo} {{name}} {{bar}}
  {/A.B}

Output:

Albert Bob root2

Combined with setting partials dynamically this would allow for a lot of really great componentized implementations.

@ElliotChong
Copy link
Author

Dynamic partials + parameters example, a bit contrived but hopefully gives the gist of being able to use partials like components.

Data:

{
    list: [
        { type: "photo", src: "http://exmaple.com/1" },
        { type: "text", header: "Text!", body: "Foo bar baz" },
        { type: "photo", header: "Photo!", src: "http://exmaple.com/2" }
    ]
}

Main Template:

{{#list}}
    {{>modal modalType="full-screen" }}
{{/list}}

{{>modal modalType="box" type="text" body="parameterized!"}}

Partials:

<!-- {{> modal }} -->
    <div class="modal {{type}} {{modalType}}">
        <div class="content">
            {{#header}}<h1>{{.}}</h1>{{/header}}
            {{> (type) }}
        </div>
        <div class="close-button"></div>
    </div>
<!-- {{/ modal }} -->

<!-- {{> photo }} -->
    <img src="{{src}}">
<!-- {{/ photo }} -->

<!-- {{> text }} -->
    <p>{{copy}}</p>
<!-- {{/ text }} -->

Outputs:

    <div class="modal photo full-screen">
        <div class="content">
            <img src="http://exmaple.com/1">
        </div>
        <div class="close-button"></div>
    </div>
    <div class="modal text full-screen">
        <div class="content">
            <h1>Text!</h1>
            <p>Foo bar baz</p>
        </div>
        <div class="close-button"></div>
    </div>
    <div class="modal photo full-screen">
        <div class="content">
            <h1>Photo!</h1>
            <img src="http://exmaple.com/2">
        </div>
        <div class="close-button"></div>
    </div>
    <div class="modal text box">
        <div class="content">
            <p>parameterized!</p>
        </div>
        <div class="close-button"></div>
    </div>

@Rich-Harris
Copy link
Member

There's a (currently undocumented! @kivikakk created a stub wiki page which I need to flesh out...) feature called 'components' which is more or less what you describe.

It works something like this (the following code will only work with the latest build).

#main

{{#list}}
  <!-- attributes on this tag are turned into data for the component. A
        current limitation is that they have to be lowercase, and you have
        to explicitly declare data (it doesn't 'leak' from the parent context -->
  <modal modaltype='full-screen' type='{{type}}' header='{{header}}' body='{{body}}' src='{{src}}'/>
{{/list}}

<modal modaltype='box' type='text' body='parameterized!'/>

#modal

<div class='modal {{modaltype}}'>
  <div class='content'>
    {{>modalContent}}
  </div>
  <div class='close-button'></div>
</div>

#modal-photo

  <img src='{{src}}'/>

#modal-text

  <h2>{{header}}</h2>
  <p>{{body}}</p>

javascript

// First, we register a new component, so that this gets rendered whenever
// Ractive encounters a `<modal>` tag
Ractive.components.modal = Ractive.extend({
  template: '#modal',

  // this is where we switch between partial types - ideally we'd be able to
  // write this logic into the template
  beforeInit: function ( options ) {
    options.partials.modalContent = options.partials[ options.data.type ];
  },

  // would be nice to do without all the document.querySelector gubbins
  partials: {
    photo: document.querySelector( '#modal-photo' ).innerHTML,
    text: document.querySelector( '#modal-text' ).innerHTML
  }
});

ractive = new Ractive({
  el: 'container',
  template: '#main',
  data: {
    list: [
      { type: "photo", src: "http://exmaple.com/1" },
      { type: "text", header: "Text!", body: "Foo bar baz" },
      { type: "photo", header: "Photo!", src: "http://exmaple.com/2" }
    ]
  }
});

The reason it's undocumented is that, as you see from the inline comments, it's not quite fully baked. But it works. There's some more discussion about it on #74.

Will close this issue but feel free to ask further questions about components etc

@ElliotChong
Copy link
Author

Very interesting, really appreciate the translated example!

Will the explicitly passed data still be be data-bound with the originating Ractive instance?

From your comment and #74 I'm guessing it's sandboxed entirely- If it is I could see it being really useful to have the ability to marshall the parent Ractive data object (binding included) while pushing any specified parameters (attributes) into a new context at the top of the stack (allowing parent data to still be access.)

Is that currently possible? Am I misinterpreting the data situation?

Thanks again!

@georgealways
Copy link

Hey Rich, many thanks for the great lib. Realize I'm on the bleeding edge here but...

with the old "rv-" style components I was able to do this:

{{#pages}}
<rv-Page sections="{{ sections }}">
{{/pages}}

Where "sections" was an array of objects and all the data would be passed down to the nested component (0.3.7). On 0.3.8 I get <Page sections="[Object object], [Object object]"> and so forth. Is this the limitation you're addressing when you say data doesn't "leak" from the parent?

@georgealways
Copy link

Aha, looks as if it's because I'm using a capitalized component name. Any particular reason to disallow that?

@Rich-Harris
Copy link
Member

@ElliotChong Yes, data binding works in both directions - if you have <widget foo='{{bar}}'/>, any instances of {{foo}} in the widget template will be bound to the value of the parent ractive instance's {{bar}}. When parent-{{bar}} changes, child-{{foo}} will also change. This works in both directions - if the widget had some logic that changed the value of child-{{foo}}, any references to {{bar}} in the parent template would change.

Re sandboxing - allowing a nested component to access 'parent' data is definitely the intention. I think it should work like closures in JavaScript, whereby a function can define its own variables but still access and modify variables from parent scopes. Not entirely straightforward to implement but it would make things much nicer.

@georgealways yes there's a reason but it's not a very good one! Basically, components were treated as a separate type of thing at parse time in 0.3.7, which led to code getting out of sync. Now, they're treated the same at parse time, and differentiated at render time, which makes certain things much easier. An unintended side-effect is that component names all get lowercased. I will flag this as a bug and see about fixing that.

@Rich-Harris Rich-Harris reopened this Nov 18, 2013
@codler
Copy link
Member

codler commented Nov 19, 2013

yep it seems components dosent work when its tag name have capital-letter in it.
A simple reproducible example: http://jsfiddle.net/6Mj8a/
Maybe thats why components didnt work with the latest build for me, ill try tomorrow.

@Rich-Harris
Copy link
Member

I forgot I'd flagged this bug - it was actually fixed yesterday with 5960bf5 (see #281). Closing

@codler
Copy link
Member

codler commented Nov 23, 2013

confirming components works in the latest build :)

@ghost
Copy link

ghost commented Jan 13, 2017

Hey guys, I finding this feature to be really slow on data updates. Or, maybe I've gone a little crazy with it.

I have a div-based table setup.

{{#each groups}}
{{>group { like 10 keys passed } }}
{{/each}}

inside group

{{#each .groupItems}}
{{>item { like another 10 keys passed } }}
{{/each}}

in item:

{{#each item.field}}
{{> field { pass another 10 or more keys } }}
{{/each}}

Updates can take like 5 seconds or more. Trying to combine the variable updates.

Is this due to the template scoping? I notice the scope paths get giant (like enough to fill up my entire console log when there's an error). Should I go back to simply using the inherited scope like before?

@fskreuz
Copy link
Contributor

fskreuz commented Jan 13, 2017

@clintonskakun suggesting opening a different issue for this so we can mark it under performance and so that it doesn't hijack a dead thread.

@evs-chris
Copy link
Contributor

What @fskreuz said, but as a brief response here, the problem is that you're creating lots of computations (object literal expressions) that are getting set as context in each partial. Direct access would be much more efficient, and using aliases would probably be pretty close in performance to direct access e.g. {{#each groups as group}}{{>group some.value as key, other.thing as otherKey}}{{/each}} where group, key, and otherKey are relatively quick to fetch and keep in sync with the model in the group partial.

@ghost
Copy link

ghost commented Jan 13, 2017

@evs-chris Thanks for the tip. Yeah the literal computations were creating some serious overhead. Swapping it out for the aliases made a giant improvement. Where it'd take 3-5 seconds to complete now does it in like <500ms. Thanks again.

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

No branches or pull requests

6 participants