component / reactive Goto Github PK
View Code? Open in Web Editor NEWTiny reactive template engine
Tiny reactive template engine
{count() > 10}
should compile to (view.count ? view.count() : model.count()) > 10
and add .demo
prop
Often need to control things like display or class of an element.
For inspiration, these are the things you can change with rivets:
See http://rivetsjs.com/#binders
This is possible with the reactive options object, but this is rather verbose, and not much better over just having a custom 'change' handler.
var model = {
labels:{
name:"leo"
}
}
Emit(model)
reactive(el,model)
setTimeout(function(){
model.labels.name = "bright";
model.emit("change labels.name","bright"); // no work
},5000)
I'm implementing this fairly standard layout with "header", "sidebar" and "content" panels:
_ _ _ _ _ _ _ _ _ _ _
| header |
| _ _ _ _ _ _ _ _ _ |
| s | |
| i | |
| d | |
| e | content |
| b | |
| a | |
| r | |
| _ _ _ _ _ _ _ _ _ _|
Currently I have a big container view that instantiates subviews and injets their dom elements one by one. It's simple albeit wordy. Is this the best approach? How you would set this up? Trying to find a good pattern, turtles all the way down.
Perhaps we need an example along these lines: http://spine-contacts.herokuapp.com/
When you pass a reactive DOM node through a data-append
or data-replace
binding, the parent call to reactive()
will re-bind to the child element. This means you can't compose reactive elements, which is a bummer.
Perhaps we need a way to say "don't bind anything on this node or its children"?
Example
var
reactive = require('reactive'),
domify = require('domify'),
html = [
'<div class="wrapper">',
'<div data-replace="sidebar"></div>',
'<div class="content">Content</div>',
'</div>'].join('\n'),
page = domify(html)[0],
sidebar = domify('<a href="#" on-click="greet">Sidebar</a>')[0];
reactive(sidebar, {}, {greet: function() { alert('Hello!'); }});
reactive(page, {sidebar: sidebar}, {});
document.body.appendChild(page);
When you click, the greet()
function will fire and so will a "no method error".
related to #44. This would be more expensive in terms of walking the dom tree to discover bindings but if we measure this out and it's reasonable it would probably be worth it. Ex:
<a href="/download/{id}">Download {title}</a>
vs
<a data-href="/download/{id}" data-text="Download {title}"></a>
e.g:
<div>{render(items)}</div>
This almost works already but is just a little broken since it gets transformed to
'<div>' + model.render(call(model, view, 'items')) + '</div>'
where's what I wanted was to call the view's render
method.
The use case is to make it easier to jump back to JS when you need the power. Though it could be used in place of computed properties in some cases.
for shared views such as dialog headings etc
ever considered this as opposed to using emitters ? (i.e. like Angular)
Would simplify a lot of things.
for me at least 90% of the use-case is this
talk about how interpolation now supports view delegation etc
https://github.com/component/reactive/blame/master/Readme.md#L122
Not seeing any example / source of it. Gave it a shot and got illegal token error.
i think there's room to make reactive a little more useful for people dealing with models (by that i actually just mean getter-setter methods) instead of simple objects.
the one principle i'm using here is that objects that are reacted to should always be accessed as objects. by that i mean that this:
<p>{name}</p>
should be the way you access the reactive object's name
property even if the property is actually a getter-setter, that should be transparent to the template. i think that makes sense, so that reactive doesnt need to care about what kind of model you're using.
if that's the case though, the current implementation (after #62) has one hole in its logic still.
<p>{name}</p> <!-- works for regular objects -->
<p>{name}</p> <!-- works for models -->
<p>{enabled ? 'yes' : 'no'}</p> <!-- works for regular objects -->
<p>{enabled ? 'yes' : 'no'}</p> <!-- DOESN'T work for models -->
in the complicated interpolation case, the expr
is determined to be !isSimple()
so then all of the props
are accessed via:
new Function('model', 'return ' + props(expr, 'model.'));
which doesn't handle methods, only regular properties. instead, our "prefixing" needs to use this kind of logic for accessing properties:
'("function" == typeof model.' + prop + ' ? model.' + prop + '() : model.' + prop + ')'
what do you all think?
data-el="methodOrProperty"
to compose existing shit like canvas
Hi,
I get the following error when I try to bind a view on the body element :
Cannot read property 'on' of undefined
It would be great to bind a view on the body element or even on the html, head, etc. For example:
<link rel="stylesheet" data-href="theme">
<h1>{name}</h1>
and have that call a function on my model/view, without having to use parens: name()
?<h1>{name()}</h1>
and have that auto subscribe to changes for me?<h1>{name()}</h1>
and have that reference View.prototype.name
instead of only Model.prototype.name
?<div class="{enabled() ? 'enabled' : 'disabled'}"></div>
and have that automatically be smart and bind a subscription to change enabled
?None of these things seem to be working for me right now.
I should mention that I'm working with component/model
and a view, not just plain objects with emitters. I can't seem to get some of this magic working. I really wish it did cuz reactive seems like potentially the coolest thing ever made.
Sorry if I'm doing something incredibly stupid.
Is there support of nested properties:
<p data-text="profile.about"></p>
var model = { profile: { about: "blabla" } };
If not is there an easy workaround or would their be a possible feature request?
Bodo
mixed in with the regular "view" methods for people who dare live on the edge and push their deps global
This would help when debugging. Would be good to print out the object as well. Most likely just using the wrong key name.
Related to #8
A common usecase for creating View helpers is simply concatenating text onto existing data… perhaps this could be handled simply in the template.
e.g.
<a data-href="id + /comments">comments</a>
Though then you might end up still needing view helper if you need to prepend text or other types of interpolation
Just a thought.
I would love to see a full featured application using reactive so I can get a sense for how to do more complicated things. Are there any out there that could be listed in the Readme/wiki?
When the textContent of the reactive DOM element changes, reactive's function overwrites the entire textContent of the DOM element. If there are other child nodes in the reactive element those get overwritten as well, e.g. or other elements that may or may not be reactive.
The simple solution to this, IMO, is to change:
this.el.textContent = "newTextContent"
to
this.el.childNodes[0].textContent = "newTextContent"
Let me know if this is okay and I'll submit a pull request.
Is it possible to have a default formatter like so:
<p class='votes' data-text='0 || votes'></p>
Re-rendering everything after each change of the model is usually too expensive.
Different models have different ways of listening to attributes (change title
, change:title
, etc), but I think this could be configured. I like how rivets does the adapter configuration, although it could be simplified and have sensible defaults.
Recently I had an issue with having JSON passed from the backend to a hidden input bound by data-text.
Reactive saw the curly braces and tried to interpolate data, instead of trigger a change event.
Perhaps we could have some attributes that allow us to ignore certain functionality? Such as data-ignore-interpolation
for example
<div class="login">
<form action="/user" method="post" autosubmit>
<input type="text" name="name" placeholder="Username" />
<input type="password" name="pass" placeholder="Password" />
<input type="submit" value="Login" />
</form>
</div>
is fine, however the following is not:
<form action="/user" method="post" autosubmit>
<input type="text" name="name" placeholder="Username" />
<input type="password" name="pass" placeholder="Password" />
<input type="submit" value="Login" />
</form>
var attrs = [
...
// remove value
...
];
bind('data-value', function(el, name){
this.change(function(){
el.value = this.interpolate(name);
});
});
why ? because when we write content at input element , then setTimeout execute el.setAttribute("value","xxx") cann't update UI view.
This would make it possible to manually implement a run-loop myself using RAF or maybe some interesting animations or undo/redo functionality maybe. Cf. #31
Or would it be better to have this feature in Emitter instead?
need to confirm but i think we're getting the callback 2+ times from some binding issue
in expressions like {siblings[@siblings.length - 1]}
. this is to prevent collisions with words in strings such as foo + "bar"
, but if we do some simple parsing we can avoid this
nicer to just do <span>Hello {user.name}</span>
etc
Considering we already have "post-processing" filters via data-attr="data | filter"
, perhaps it makes sense to be able to pre-process data as well.
For example, say all Dates should be formatted in a particular way, rather than having to add this filter to every date output in my template, I could do something more generic, such as:
reactive(el, data, view)
.filter(function(content) {
if (content instanceof Date) return moment(content).format('LLL')
return content
})
or perhaps I want to handle undefined/null in a particular way (this solves #11, #8):
reactive(el, data, view)
.filter(function(content) {
if (content == null) return ''
})
Possibly related to #6.
Reactive.prototype.set = function(name, val){
...
this.renderElement( el, obj[name](), name );
...
}
// By default we just set the textContent
// but perhaps we might want to do something else?
Reactive.prototype.renderElement = function( el , value, name ) {
el.textContent = value
}
Right now they will show up literally as undefined
, null
, & false
. Personally I think that undefined
and null
should probably just be ignored (use the defaults). So in:
<span class="tag" data-text="tag">tag</span>
<input class="url" data-value="url" placeholder="url...">
These values would default back to tag
and blank (leading to url...
as placeholder).
I think false
might be good to either show or not show, but I think that's covered in another attribute.
to reactive instead of doing it in the template, this is necessary for our interpolation support as well
for special-cases
Adding child items to the parent element manually sucks. Also means the list itself isn't reactive, only it's elements (and only while they exist).
Also made more painful by #5 because you have to do a little dance to remove the parent and render the child element (e.g.<li>
or <option>
) as in example below:
<!-- Target Result -->
<ul>
<li data-text="name">Tim</li>
<li data-text="name">Bob</li>
</ul>
<div id="user-item-template">
<li data-text="name"></li>
</div>
var userItemTemplate = document.getElementById('#user-item-template').innerHTML
var parentListEl = document.getElementById('#parentList')
var users = [{name: 'Tim'}, {name: 'Bob'}]
users.forEach(function(user) {
var itemEl = domify(userItemTemplate)
reactive(itemEl, react(user))
itemEl = itemEl.children[0] // Remove el from parent element. Gross.
parentListEl.appendChild(itemEl)
})
I guess copying rivet's style again wouldn't be too bad:
<ul>
<li data-each-todo="list.todos">
<input type="checkbox" data-checked="todo.done">
<span data-text="todo.summary"></span>
</li>
<ul>
What do you think? Is there a better API?
This project looks awesome. I'm excited to use this instead of Ember soon.
I get the goal of component
in general, keeping the modules small and accomplishing one specific task. But I'm wondering what you guys think of including a run-loop (like in Ember).
Basically, instead of emitting events and having them directly update the DOM, you wait until the next frame to update the DOM in one big chunk. To do this, it requires:
status
and the properties all changed from pending
to active
. With the current implementation it would update the DOM one row at a time, which slows down as there are more elements. Eventually this method slows to a crawl (even with a modest number of elements). From my knowledge the ideal way of handling this is to remove the parent-most DOM element containing the elements that will be updated, and then updating all of the properties, then re-appending the DOM element to the page. Is this correct?What are your guys' thoughts on the ideal implementation for this? It seems that this sort of "run loop" would be implemented at the level of the require('emitter')
, which wouldn't actually dispatch the events until the next frame. Not sure though.
reactive renders an undefined value as 'undefined' which is problematic in many ways. i saw that jade handles this case with an empty string which i think is a much better solution.
lots are really unlikely to clash
this is a common use case i find myself doing:
<div class="integration">
<nav class="integration-nav" data-replace="replaceNav"></nav>
</div>
View.prototype.replaceNav = function () {
var el = magic();
dom(el).addClass('integration-nav');
};
would be cool if the replaced element just carried over the other elements classes, since i can't think of reason i'd apply a data-replace
and not want that functionality.
changsets instead of reactive.subscribe(function(obj, prop, fn){
Angular has this "2 way binding" feature where if you change the value of the thing that is bound, then the object it was bound from is also updated, might warrant another "cooperating" component. How would you go about implementing this?
kinda lame and goes against localized code, if you want to format something it might as well just be on the view (or base view)'s prototype
Can't recall if we've had this discussion before, but if component/to-function
was used to evaluate Binding.prototype.value, it would be possible to do stuff using expressions like:
<a href="#" data-hide="users.length < 10">More</a>
Could put this in the options/fns object passed to reactive. How do you trigger a change for an expression? I don't know. Perhaps some kind of loop ala angular (#26, #31).
This will conflict with "computed properties" syntax, but I'm not really sure what's going on there. Ugh this is probably a bad idea I guess.
vs the manual syntax
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.