GithubHelp home page GithubHelp logo

reactive's Issues

Support properties other than value

Often need to control things like display or class of an element.

For inspiration, these are the things you can change with rivets:

  • text
  • html
  • value
  • show/hide
  • enabled/disabled
  • checked/unchecked
  • any attribute
  • classes

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.

layouts and nested views

I'm implementing this fairly standard layout with "header", "sidebar" and "content" panels:

_ _ _ _ _ _ _ _ _ _ _
|       header       |
| _ _ _ _ _ _ _ _ _  |
|  s  |              |
|  i  |              |
|  d  |              |
|  e  |    content   |
|  b  |              |
|  a  |              |
|  r  |              |
| _ _ _ _ _ _ _ _ _ _|
  • "sidebar" is a dynamic list of links. It changes based on filters set in the "header"
  • "content" displays some content. This may contain sub-views itself (e.g. tabs along the top) and or subview widgets for handling different sections of the content.
  • "header" displays the title of current content, filter buttons and some persistent links, e.g. to "help".

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/

Events get bound multiple times with nested reactive elements

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".

consider removing data-* in place of interpolation

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>

support passing arguments to methods

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.

dirty data checking

ever considered this as opposed to using emitters ? (i.e. like Angular)

Would simplify a lot of things.

better docs

talk about how interpolation now supports view delegation etc

fixing interpolation of model attributes

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?

Binding on body (html, head, etc)

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">

a little confused from the docs

  1. Am I supposed to be able to do this: <h1>{name}</h1> and have that call a function on my model/view, without having to use parens: name()?
  2. Am I supposed to be able to do this: <h1>{name()}</h1> and have that auto subscribe to changes for me?
  3. Am I supposed to be able to to this: <h1>{name()}</h1> and have that reference View.prototype.name instead of only Model.prototype.name?
  4. Am I supposed to be able to do this: <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.

support of nested properties

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

+ operator for concatenating text

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.

Advanced examples?

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?

Child nodes overwritten in reactive element

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.

attribute-specific rendering

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.

Issue with JSON / interpolation

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

support root-level selectors

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>

'data-value' use el.value replace el.setAttribute

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.

Pause and resume DOM operations globally

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?

remove the need for @

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

Preprocessing data

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.

Refactor rendering?

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
}

handling undefined, null, false for data-text & data-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.

add .computed()

to reactive instead of doing it in the template, this is necessary for our interpolation support as well

Iteration API

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?

Thoughts on a runloop?

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:

  • Grabbing the property/method value on the next frame, rather than emitting the event and grabbing the property all in the current frame (examples/computed.html#L58). This way if the property changes multiple times, the DOM only gets modified once.
  • Having the dom render/paint only once when any number of properties change. I'm not sure exactly how browser rendering works, but say you had a table with 20 rows with a column 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.

present undefined values as empty string

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.

idea: carryover replaced elements classes

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.

2 way binding?

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?

remove format support

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

integrate to-function

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.