Giter Club home page Giter Club logo

ractive's People

Contributors

amitaber avatar anton-ryzhov avatar aphitiel avatar codler avatar dgreensp avatar evs-chris avatar fkmhrk avatar frk2 avatar fskreuz avatar giovannipiller avatar guilhermeaiolfi avatar heavyk avatar imyelo avatar jonvuri avatar lluchs avatar m-r-r avatar madgvox avatar marcalexiei avatar marcello3d avatar martinkolarik avatar martypdx avatar monoblaine avatar paulie4 avatar rich-harris avatar rstacruz avatar sabob avatar seijikun avatar thomsbg avatar tombyrer avatar writhe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ractive's Issues

Text in SVG elements

This doesn't work:

<svg xmlns='http://www.w3.org/2000/svg'>
    <text>some text</text>
</svg>

Off the top of my head, probably because it's trying to use innerHTML

Access to the current soon to be updated value in formatters?

Hey there, thanks for the awesome library. It's damn near everything I've been looking for!

I was wondering if there was any way to access the current value of the data property before it's updated?

My rationale is that it would open up running arbitrary operations on these templated values like incrementing counters instead of just replacing data.

I noticed this in the Anglebars.Data.prototype.set() data.js:

// find previous value
previous = this.get( address );

Maybe there's a way to do this?

throws error when el isn't found

i'm porting my app to use anglebars and in the previous render logic I had some templates that would silently fail if the target element wasn't in the DOM. I could insert some logic to check if they are in the DOM before I instantiate a new Anglebars instance but figured I'd ask if you think this behavior is expected or if its a bug

partials

Not sure if it's working (or I'm missing on usage), I see it's implemented in the code...

Better parse errors

Ideally it should be possible to pinpoint which part of a template (i.e. which character, or token) caused the parser to fail

Map elements to parts of the viewmodel

See #6. It would be useful to be able to establish which part of the viewmodel (which todo, in the canonical example) is related to which part of the DOM. Need to figure out the best way to do this in a way that is a) performant (ie avoids too much DOM traversal) and b) philosophically coherent, ie doesn't turn Anglebars into a full-blown state management utility

Mustache-less templates and elements with id attributes

As a convenience, Ractive stores references to nodes with id attributes as ractive.nodes[id]. This doesn't happen when relevant parts of the template don't contain mustaches, since everything is bunged in with innerHTML and there is no opportunity to identify nodes with an id attribute.

Best solution is probably to mark such nodes as un-stringifiable at parse time.

Modified arrays and downstream changes

Setting properties downstream of an array or object that has been modified doesn't result in the children of the modified object being updated. I.e. if you have a template like this

{{#list | uppercase}}
{{.}}
{{/list}}

with data like { list: [ 'foo', 'bar', 'baz' ] } (where 'uppercase' uppercases each array member) then the result will look like

FOO
BAR
BAZ

If you then do view.set( 'list[2]', 'boo' ) nothing happens until the whole array is updated.

We need some way that modified objects can be made aware of changes to members of those objects (and their descendants).

Implicit array generation via .set()

Calling view.set('foo.bar', 'baz') on a view with no data causes this viewmodel to be created:

{
  "foo": {
    "bar": "baz",
  }
}

This is fine, however view.set('foo.bar[0]', 'baz') results in

{
  "foo": {
    "bar": {
      "0": "baz"
    }
  }
}

when it should ideally result in

{
  "foo": {
    "bar": ["baz"]
  }
}

Multiple expressions sharing the same virtual keypath

If there are two expressions that resolve identically, they shouldn't both be listed as dependencies of their keypaths - first come, first served. The second mustache will be taken care of by the first expression's evaluator.

Strip whitespace from templates

Templates often contain lots of whitespace:

<div>
  <p>This template   contains      unnecessary   whitespace.</p>
</div>

The example above is functionally equivalent to

<div><p>This template contains unnecessary whitespace</p></div>

because browsers don't care about neat indentation, and unless you're using white-space: pre[-wrap]; multiple whitespace characters are collapsed.

So it would be useful to strip unnecessary whitespace out during the compilation step, unless a) the user has explicitly chosen to preserve whitespace, or b) the whitespace is inside a <pre> element. This means

  • collapsing multiple consecutive whitespace characters
  • Eliminating whitespace immediately after an opening tag, and immediately before a closing tag

Allow promises-style async code as well as callbacks?

With the introduction of transitions there are a number of potentially-async operations (set, update, teardown etc). Would be nice if these returned promises, as they're generally nicer to work with than callbacks. Only question is whether it's worth the overhead

Double compiling in mustache-less templates

Ractive will try to compile any templates that are passed in as strings - if they are objects, it assumes they are valid precompiled templates.

This is fine unless the template is HTML (not SVG) and doesn't contain mustaches, since the compiled template will be a string, not an object (since this allows use of innerHTML). So at runtime Ractive tries to compile it again!

Prepending mustache references with '.' to enforce current context

By default (and as per the mustache spec), if a property is not found in the current context, Ractive travels up the context stack until a reference can be resolved. This isn't always what you want (e.g. when using recursive partials).

This syntax could be made to mean 'only look for this property in the current context':

{{.name}}

In practice this means we can always resolve such a reference immediately - it's just context + reference - which actually saves us some work.

manual update issue

If we have the following data

{ total_completed: 0,
  todos: [{todo:'beep', completed:false}]
}

If I manually change the value of total_completed (on the underlying data), then tpl.update('total_completed') - template won't render the new value.

Pushing new values to the todos array, followed by tpl.update() works though.

Store references to DOM nodes

I don't know if this violates the YAGNI principle, but it might be quite neat if references to ID'd nodes were stored, e.g. on a view's nodes property:

<div class='box'>
  <a id='close'>X</a>
  {{{content}}}
</div>
view = new Ractive({
  el: 'container',
  template: myTemplate,
  content: '<p>some content</p>'
});

console.log( view.nodes.close ); // logs the anchor with the X

Maybe class names could be arrays on the nodes property, containing all the nodes of that class.

Just an idea. Easy to implement, question is whether it's necessary or not.

Declarative DOM event binding

This is a common pattern:

<div class='dialog'>
  <p>{{dialogText}}</p>
  <a id='okay' class='button'>OK</a>
</div>
view = new Ractive({
  el: '#container',
  template: dialogTemplate,
  data: { dialogText: 'Press OK to close this dialog' }
};

document.getElementById( 'okay' ).addEventListener( 'click', function () {
  view.teardown();
});

This is fine, as far as it goes, and using jQuery or similar makes it slightly less verbose. But the fact that we're giving an element an ID just so we can easily find it a moment later and bind event handlers to it is a bit... yucky. It goes against the spirit of declarative programming to have to throw in these hooks. Sure, the ID could be more descriptive - 'closeDialog' or something - but that's not really what IDs are for. Our abstractions are leaking.

It would be nicer to be able to do something like this:

<div class='dialog'>
  <p>{{dialogText}}</p>
  <a on-click='closeDialog' class='button'>OK</a>
</div>
view = new Ractive({
  el: '#container',
  template: dialogTemplate,
  data: { dialogText: 'Press OK to close this dialog' }
};

view.on( 'closeDialog', function () {
  this.teardown();
});

That way things are better separated. It also allows us to define multiple behaviours in a nicer way:

view.on({
  collapse: function () {
    // code goes here
  },
  expand: function () {
    // code goes here
  }
});

Some additional thoughts right off the bat: it would be useful to pass along the event data, and also the element that was the subject of the DOM event (i.e. the event's target or one of its ancestors). Normally with event handlers, this is the element, but within a view.on() handler this === view, and it probably isn't a good idea to change that. A good compromise would be to pass along the element as an argument:

view.on( 'open', function ( event, el ) {
  var target;

  if ( event.shiftKey ) {
    el = el.getAttribute( 'data-target' ); // or whatever
    // do something
  }
});

So. The real problem to figure out here is what syntax to use in templates to make this happen. It's probably best to use an attribute, as many editors barf if you start throwing illegal characters around inside a document. The colour coding gets messed up. That matters, because the moment that starts happening everything begins to feel like a hack.

For that reason I'm not all that keen on something like Ember's {{action}} helper.

Angular does it using an ng-click attribute (though rather than an event label, you're using pseudo-JavaScript which corresponds to a method on the relevant controller's $scope object). This isn't a bad solution - it means the template doesn't validate as HTML, but who really cares? We could do something similar with rv-click, or something. I'm not in love with the aesthetics though.

One thing we definitely shouldn't do is use an on-click attribute or similar, example above notwithstanding. That's too similar to onclick where the value is eval'ed as JavaScript in the global context. Ugly, ugly, ugly stuff.

Two final considerations:

  • Do we need a way to pass arguments along? If not, we can still grab data from the DOM, but that's not totally ideal.
  • Do we support custom events? E.g. I often define a 'tap' event which normalises behaviour between mouse and touch interfaces, and prevents mousedown-waggle-hold-mouseup sequences from being interpreted as clicks. If so, how?

sections in attributes output an empty string

{{#todos:i}}
   <li data-index="{{i}}" class="{{#completed}}completed{{/completed}}{{^completed}}view{{/completed}}"></li>
{{/todos}}

the above will always render class="", whereas as the same section renders correctly inside the li tag.

issue #14 (still not 100% fixed)

The fix seems to have solved the initial rendering issue:

<li data-index="{{i}}" class="{{#completed}}completed{{/completed}}{{^completed}}view{{/completed}}">
</li>

Initial output is correct (first render), but if .completed changes, class attribute in the above example doesn't re-render.

This is specific to attributes because it works if we take the same content of class attribute and place it inside the li tag itself.

PS: Hope I'm not driving you crazy with me I'm working with Angelsbars that's why I'm seeing this ;)

Removing unnecessary whitespace

We could happily remove whitespace-only text nodes at the beginning or end of a template. We could probably also remove whitespace between certain elements (e.g. newlines between divs) but there are lots of rules here that I don't fully understand...)

Priority is backwards!

It feels like the way priorities are expressed in the dependency graph is backwards. Instead of listing priorities by dependency, shouldn't we be listing dependencies by priority?

{{condition?iftrue:iffalse}}

This syntax would be much nicer to deal with than {{#condition}}iftrue{{/condition}}{{^condition}}iffalse{{/condition}}. Real world use case - todo list:

<li class='{{done ? done : pending}}'><!-- etc --></li>

Obviously not part of mustache, but would be a useful extension.

Question: should the values be quoted?

Template scripts on the page

For quickly setting up and testing things, without faffing about with AJAX/requirejs/etc, it would be useful to be able to refer to templates embedded on the page like so:

<script type='text/template' id='myTemplate'>
<!-- template content goes here -->
</script>

This could then perhaps be referenced by passing in template: '#myTemplate' as an initialisation option

Adaptors

It would be good to be able to configure Ractive to work alongside any event-emitting data source (like Ractive Couch but with any back-end/model).

Inspired by Rivets.js, I like the idea of using adaptors, where the adaptor is a simple layer that wraps around the source's API.

This is fairly simple in concept. The trick will be making it easy to create new adaptors.

formatters are no longer reactive in latest version

a.formatters.lower = function (input){        
     return input.toLowerCase();
 };

The above seem to run only once, i.e. if the value of "input" changes later the above will not be re-rendered. This used to work in the previous version.

Subclass data should be extended, not overwritten

If you do

Subclass = Ractive.extend({
  template: someTemplate,
  data: { foo: 'one' }
});

view = new Subclass({
  el: '#container',
  data: { bar: 'two' }
});

you would expect view.get() to return { foo: 'one', bar: 'two' }. Instead it gets overwritten, so it returns { bar: 'two' }.

This is a side-effect of the lazy way Ractive initialises - all properties of the options argument get added to the instance. Should probably fix that.

Conditional attributes

See #6. We want to be able to add or remove element attributes (rather than merely changing their value) when parts of the viewmodel change, the obvious example being

<input type='checkbox' {{#complete}}checked{{/complete}}>

or even

<input type='checkbox' {{checked}}>

update docs & tutorial

Ping!

Just noticed Ractive hit 3.0! Congratulations Rich! Looking forward to seeing what's under the hood! Already noticed proxy-event="" -> awesome, means I can get rid of my custom implementation (x-bind="" then I add event listeners once the page has rendered).

Ideas for future implementations:

  1. Inline partials
  2. Components

Keep it up!

Proper dependency tracking

There is an upper limit to the complexity of applications that Ractive can be used for without some more sophisticated dependency tracking, a la Knockout, Statesman or Reactor.

Right now the only form of dependency tracking is downstream keypaths (e.g. foo.bar) depending on their upstream keypaths (e.g. foo).

Better dependency tracking could manifest itself in two ways - computed values which are not considered 'downstream' of a particular keypath, and modifiers which have one obvious dependency (their upstream value) but can have other dependencies.

Knockout and Reactor track dependencies by watching for model.get( property ) calls within functions. This is really clever but I'm not totally sure I like it, it's a bit voodoo-y and requires that dependencies be re-tracked each time the function is called (though if the computing function has lots of branching logic that may be a win).

The alternative is to make dependencies explicit somehow. In Statesman computed values are described as e.g. ${x} + ${y} - the dependencies are extracted using regex - or declared as a dependsOn property.

Not totally sure at this stage what direction to head in.

Server side rendering

Angelbars is AWESOME!! Well done!!

I got Angelbars to render server-side using jsdom for serving static content to crawlers, to do that I simply passed a jsdom document to the constructor options and declare a global variable "document". The change is too small to send a PR request:

if ('undefined' === typeof document) var document; (global - alternatively at the environment check at the bottom of the script)

if (options.document) document = options.document; (in the constructor)

I'm working on a framework for building P2P-based, realtime single-page and intending to use Angelbars; let me know if you're interested in seeing this in action ;)

Sanitising templates

It would be useful to have the option of sanitising templates, particularly for cases involving user input. These elements could potentially be put to nefarious ends:

  • script
  • style
  • link
  • iframe
  • ...anything with an href or src attribute?

Should be fairly straightforward to remove these during the compile step

breaks on empty values

<input type="text" value="">

Rendering the above will fail, giving the following error:
TypeError: Cannot call method 'toLowerCase' of undefined.. at Anglebars.js:1355:70)

That attribute is there, but value is an empty string

index mapping fails if index is in an attribute

{
        "name": "Section with index refs",
        "template": "<ul>{{#items:i}}<li data-index="{{i}}">{{name}}</li>{{/items}}</ul>",
        "data": {
            "items": [{"name":"zero"},{"name":"one"},{"name":"two"}]
        },
        "result": "<ul><li data-index="">zero</li><li data-index="">one</li><li data-index="">two</li></ul>"
}

Notice I moved the {{i}} from the original test and placed it in an attribute data-index

Allow lists directive to iterate over object properties

This idea is born out of using ractive for about a day. So feel free to shoot it down in flames.

Once challenge I have noticed is managing large lists. When updates/deletes come in, one has to find the row in an array that matches the update so one can view.set('row[0]', update).

One idea to make things easier would be to use the id that these objects have (from a datastore) and allow the list directive to iterate over object properties. For example, one would map data back from a db like this

view = new Ractive({
  data: {
     rows: {
          '434342' : { id: '434342', name: 'Ryan' },
          '434343' : { id: '434343', name: 'Rich' }
     }
  }
});

and the template

   <ul>
     {{#rows}}
          <li data-id={{key}}>{{value.name}}</li>
     {{/rows}}
   </ul>

and to do an update

view.set('data.rows["434342"]', {id: '434342', name: 'Rob'});

This is non-standard mustache but I think it makes sense in terms of allowing data-binding with large lists.

Transitions, and list update behaviour

We can theoretically control how elements are rendered and torn down. For example on the todo list demo it would be neat if new todos could slide in, and removed ones could fade out, or something.

Or a sequence of images could have a staggered fade in. Fantasy code:

{{#images:i}}
  <img class='thumb' src='{{src}}' data-index='{{i}}' intro='staggeredFade'>
{{/images}}
.thumb {
  transition: 1s opacity;
}
ractive = new Ractive({
  el: container,
  template: theTemplate,
  data: { images: someImages },
  transitions: {
    staggeredFade: function ( el ) {
      var index = +( el.getAttribute( 'data-index' ) );
      setTimeout( function () {
        el.style.opacity = 1;
      }, i * 100 );
    }
  }
});

Of course this is quite a naive example - you might want different transitions at different stages of the app (initially you might want the staggering, but if new images were added after that then maybe you'd want them to appear straight away, for example).

CSS animations should be the default, but being notified of transition ends tends to be a bit of a ballache (though that's an opportunity for Ractive to take care of some unpleasant grunt work for developers, if we can find a nice way of doing something like ractive.set( keypath, value, transitionEndHandler ). That requires further thought.)

Lists

We'd also want to change the way lists behave. At present, if you mutate an array, the wrapped mutator methods call ractive.set( keypath, myself ) with whatever keypaths (and indeed whatever Ractives) the array is currently bound to. That means that if you do list.shift(), instead of removing the zeroth section fragment, it removes the last one, and updates all the fragments in between so that it looks like the top item was removed.

The reason for this is that updating all the dependency tracking would be a bit of a ballache. However it's almost certainly the Right Thing To Do. If we can save ourselves some DOM manipulation, that will pretty much obliterate the overhead of reassigning dependencies, as well as enabling transitions.

That's fine for lists updated using array mutator methods - situations where an array gets updated and set or updated are a little tougher. How to we know which section fragment belongs to which list item? This also requires further thought.

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.