-
Notifications
You must be signed in to change notification settings - Fork 396
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
Allow binding nested Ractive objects #74
Comments
It's something I've thought about - it would indeed to great to keep modules separate but update them all simultaneously. I haven't quite figured out an approach I really like yet. You can use partials however. There's some info about it on the wiki, and a tutorial here - hopefully this will do what you need. I'll leave this issue open for the time being to remind me to think about it some more! |
I saw that I can use partials and they can help, however, that's not quite as powerful, since it's only the template that is encapsulated in the partials, not things like event handlers and data. |
+1 I think the ability to nest Ractive instances will be really useful for building complex applications. If they can be nested then they really become reusable widgets, encapsulating a view and behaviors. |
👍 ...what @jpellerin said. I'd actually argue that, for the sake of promoting, encouraging, and allowing reusability, this would be beneficial for any type of application...complex or simple. |
Thanks for your input everyone, it's very helpful - looks like this is a popular idea. If anyone has any specific use cases they can share, it would help me/us figure out the best design. |
I was about half-way through writing my own custom templating & data bindings library when I discovered Ractive and realized it does 90% of what I want already- this is one of the things I'm missing, though. What I'd find particularly useful is if nested property objects were automatically turned into sub-Ractives. E.g., such that given var ractive = new Ractive({el: my_element, template: my_template, data: { nested_data: { name: "Bob" } } }); ractive.data.nested_data is itself a ractive containing the binding for "name". For purposes of explicitly nesting Ractives within each other by passing one in as data to another, it would be really nice to have some kind of compiled-but-not-instantiated ractive (combination of template, initial data, and any observers / event listeners) that could be used to instantiate multiple copies of a Ractive widget without having to re-do the behavior setup every time. (It seems to me that creating subclasses with .extend() comes close to this idea, but just falls short of reaching it without the nesting component). |
+1 |
@gliese1337 I suppose when you say "pass [the nested ractive object] to another part of the program", you want to observe the updates to a property in the object? Otherwise passing the plain object would work. This would probably require separating the change event handling and the template/binding of a Ractive, since normally it is expected that a Ractive has an associated template, but it would not know what template to use if it created them automatically. I personally don't see the need for automatically creating sub-ractive objects, since normally they would need there own (explicitly defined) Ractive class with the associated template, event handlers, etc. |
@conradz Either observe updates, or cause updates. Just passing the plain object doesn't work if you want to call .set(). |
+1 |
Hi there early adopter friends, I've had a go at implementing some of these ideas. This is a first stab, so nothing in here is written in stone - at this stage I'd love to get some feedback on whether you think this is the right direction. I'm calling it 'components', as the idea is fairly similar to Web Components (also Ember.Component). Here it is in action (click on the template/javascript tabs above the readme to see how it was put together). Basically the idea is that you define a component constructor using Ractive.extend()... MyWidgetConstructor = Ractive.extend({
template: '<h2>I am a widget!</h2>'
init: function ( options ) {
// any widget-specific setup goes here...
this.specialWidgetMethod();
},
specialWidgetMethod: function () {
// as the design stands, there is no way to get a reference to this
// widget instance, so all the behaviour needs to be set up during
// the init sequence - you can't call this method externally
}
}); ... and register it like so: ractive = new Ractive({
el: 'main',
template: '#tpl',
components: {
widget: MyWidgetConstructor
}
});
// or by doing Ractive.components.widget = MyWidgetConstructor, though this isn't implemented yet Then you can include the component like this: <h1>Here is my widget</h1>
<rv-widget/> In this case, the widget is completely isolated from the surrounding context. However we can create an interface like so: <rv-widget foo='bar' size='5' options='{"some":"json"}' dynamic='{{someData}}'/> This would be the equivalent of doing the following (in fact this is basically what happens under the hood): widget = MyWidgetConstructor({
el: theParentElement,
append: true,
data: {
foo: 'bar',
size: 5,
options: {
some: 'json'
},
dynamic: someValue // whatever {{someData}} resolves to
}
}); Whenever We can also listen to events coming from the widget: <rv-widget on-foo='bar'/> Then, whenever the widget fires the So, that's about the size of it so far. Would love to know if you all think this is the right way to go about solving this (rather tricky!) problem. |
Where does the name of the tag 'rv-widget' get specified ? On Wed, Aug 7, 2013 at 3:14 PM, Rich-Harris [email protected]:
|
Fantastic! This is great! But I wonder if it can be in Mustache syntax and maybe replace partials? |
@weepy , from here
|
@weepy - right now, there's a fairly crude mechanism: at parse time, any elements whose tag names begin Incidentally, this was just a simple hack to get up and running. In the longer term I don't know if prepending all component names like this makes sense - it's certainly useful to know straight away if something is an element or a component (without having to look up every tag name the renderer comes across), but the @codler I thought about that - initially I tried something like The problems came when defining the interface between the parent ractive and the component, I found myself contorting the syntax into all sorts of weird shapes. Fundamentally the problem is that components don't really behave like partials - components are isolated blocks of functionality with their own models, whereas partials are just shorthand - they inherit the model from the surrounding context. For components to be truly reusable I think they need to be isolated in this way, and without normal partials Ractive wouldn't be backwards compatible with normal Mustache templates (which is one of the design goals). But it's interesting that we both leaned in that direction initially. Can you describe what the benefits would be, as you see them? |
I suggested Mustache syntax because it would be more consistent but maybe it is not possible. I have been thinking and came up with an illustrative example on how we maybe could use lambda think js
tpl
output
|
I like this addition. I've got several immediate uses for it. It doesn't feel like something I would want to use for a one-off purpose, though. E.g., embedding a sub-ractive as the body content of a modal dialog ractive- I wouldn't want to create a component constructor for the dialog body, because it's never going to be used anywhere else. That's kind of an inverse situation- rather than a re-usable component embedded in a larger custom template, there's a smaller custom template embedded in a larger reusable component. For that kind of use case, it makes a lot more sense to just provide the smaller embedded ractive object as part of the data. |
To what extent can I write a fully reusable Reactive component. E.g. ValidatedInputControl that a 3rd party can just include in their project and use? |
I've added a stub glossary entry for components just so people can be made aware of their existence other than by stumbling upon this issue/the examples page (as I did). Hope this isn't too forward! |
I wasn't aware of this feature, so this happened... https://github.com/dshimkoski/ractive-tree This approach is a bit different from the one above though. The code basically rips all ractives out of the DOM, children first, sticks them in temporary hidden elements, then inits everything from the root up, creating parent/child links in the ractives along the way. This isolates each ractive. One might be embedded in another, but that's the only relationship they have by default. I like this because there's no magic involved. You could still have a child ractive look to its parent for data, but this would have to be configured explicitly. Another benefit of this approach is there would be no need to introduce another concept, i.e., no 'components'. It's just ractives all the way down. I dislike the code I posted because it establishes parent/child relationships with hacky DOM stuff when you're already figuring this stuff out inside the library. Instead of this, it would be nice if I could provide a factory that Ractive calls when it encounters a new element. If the factory returns false, it's a normal element, otherwise a Ractive subclass is expected. Parent/child links are updated and that's it. With a tree of ractives all rigged up, I can hang additional functionality on it without requiring any changes to the library. I can probably do most of that stuff in the factory of course, but if I need to traverse the tree for some reason, it would be available to me. |
@Rich-Harris is something supposed to happen when I hover over the donut charts? Nothing happens on Firefox 25 or Chrome 31 on Ubuntu 13.10 JavaScript Console says http://www.ractivejs.org/examples/components/libs.js is a 404 |
Closing this issue as most of the functionality discussed here is now available in components. There are still a few problems to work out, which deserve their own issues eventually, but for now I'll jot a few thoughts down: What to do with markup inside a component?It would be really great to be able to do stuff like this: <leaflet lat='{{lat}}' lon='{{lon}}' zoom='{{zoom}}'>
<leaflet-tilelayer tiles='http://{s}.tile.cloudmade.com/API-key/997/256/{z}/{x}/{y}.png'/>
<leaflet-marker lat='51.5' lon='-0.09' popup='<b>Hello world!</b><br>I am a popup.'/>
</leaflet> At the moment, markup inside a component is effectively 'swallowed'. Finding componentsWe could argue that components should be entirely self-contained and data-driven, but the reality is it is sometimes necessary to interact with a component directly within an app. Right now, there's no way to find a component, so there's no way to interact with it - the closest we can get is widgets = [];
Ractive.components.widget = Ractive.extend({
init: function () {
widgets.push( this );
},
foo: function ( bar ) {
alert( bar );
}
});
// later...
widgets[4].foo( 'woo-hoo!' ); There's a Access to parent data contextIt's a pain to have to do stuff like this: <widget foo='{{foo}}' bar='{{bar}}'/> At the same time, it's good to be able to treat components as isolated sandboxes. There's a useful analogue here which is JavaScript closures - a function can access (and modify) variables from parent scopes, but variables declared inside it are free from external meddling. Would be neat to have the same thing with components. Arbitrary render methodsRight now, components are assumed to work the same as Ractive as a whole - template and data goes in, DOM comes out. It would be nice to do away with this assumption, so that a Ractive component could describe (for example) a canvas chart or (why not?) a three.js scene. This, and the first point above (the leaflet.js example) would be a win from an app developer's perspective because the interface for working with UI components would be separate from the technologies and libraries used - more power, less to learn. That's all that springs to mind, though doubtless there are other ideas that will also deserve attention. Obviously feel free to carry on the discussion on this thread. |
Make partials first class (see #236 (comment)). Bind
Or, since partials are first class, we could transform them by rendering an expression:
#280 suggests a solution to this, by repurposing the id and class parameters. |
Since ractive.find returns a node and a componant instance is not a node it would be strange if find would return different "datatype". |
Is it currently possible to bind nested Ractive objects? I was looking at the source and while it didn't look like it would be hard to support, I couldn't find anything that does it.
What I'm trying to do is have Ractive objects in the "root" Ractive object, and then bind to them from the template. The output would be the rendered (and bound) template of the "child" Ractive object.
Example JS:
The output from this would be "Hello World!".
This would be really useful for reducing template duplication, and would allow breaking up the views into sub views. It also helps a lot when having lists (for example, each row in a table could be its own Ractive object with its own logic and template).
The text was updated successfully, but these errors were encountered: