Giter Club home page Giter Club logo

rivets's Introduction

Rivets.js

Rivets.js is a lightweight data binding and templating system that facilitates building data-driven views. It is agnostic about every aspect of a front-end MV(C|VM|P) stack, making it easy to introduce it into your current workflow or to use it as part of your own custom front-end stack comprised of other libraries.

Usage

<section id="auction">
  <h3>{ auction.product.name }</h3>
  <p>Current bid: { auction.currentBid | money }</p>

  <aside rv-if="auction.timeLeft | lt 120">
    Hurry up! There is { auction.timeLeft | time } left.
  </aside>
</section>
rivets.bind($('#auction'), {auction: auction})

Getting Started and Documentation

Documentation is available on the homepage. Learn by reading the Guide and refer to the Binder Reference to see what binders are available to you out-of-the-box.

Building and Testing

First install any development dependencies.

$ npm install

Building

Rivets.js uses gulp as its build tool. Run the following task to compile + minify the source into dist/.

$ gulp build

Testing

Rivets.js uses mocha as its testing framework, alongside should for expectations and sinon for spies, stubs and mocks. Run the following to run the full test suite.

$ npm test

Contributing

Bug Reporting

  1. Ensure the bug can be reproduced on the latest master.
  2. Open an issue on GitHub and include an isolated JSFiddle demonstration of the bug. The more information you provide, the easier it will be to validate and fix.

Pull Requests

  1. Fork the repository and create a topic branch.
  2. Make sure not to commit any changes under dist/ as they will surely cause conflicts for others later. Files under dist/ are only committed when a new build is released.
  3. Include tests that cover any changes or additions that you've made.
  4. Push your topic branch to your fork and submit a pull request. Include details about the changes as well as a reference to related issue(s).

rivets's People

Contributors

adamdz avatar ansman avatar atsar avatar awinterman avatar benadamstyles avatar blikblum avatar chadhietala avatar cjblomqvist avatar davidcl64 avatar davidmcclure avatar der-on avatar evindor avatar feelepxyz avatar fnd avatar gavinballard avatar gianlucaguarini avatar jccazeaux avatar kayhadrin avatar mikeric avatar nickjs avatar obmarg avatar paulj avatar pgib avatar tcz avatar terrancesnyder avatar vvo avatar xori avatar zayec77 avatar zmbush avatar zowens 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

rivets's Issues

Delayed value on inputs

It would be nice if you could specify when you want 'value' to be updated (keyup, keydown, keypress, etc.) to make everything look a bit snappier. Right now you need to lose focus/press enter before it will publish using data-value. I tried to accomplish this with an extension but modifying value within the context of the function has no effect. This might be a different issue but it would be nice to have control over changing the value from within an extension via some convenience functions.

What I want to do (rough example):

rivets.register 'keyup', (el, val) ->
  el.addEventListener 'keyup', (e) -> 
    val += String.fromCharCode e.which

Two-way binding calling adapter publish even though bypassed

I'm doing data-checked="todos:somefn < todos.somevar"

When I check the box it does a publish(todos, "somefn", true) or uncheck publish(todos, "somefn", false)

Shouldnt using :somefn bypass the adapter entirely and turn it into a one-way bind?

Allow arguments for computed properties

Here's an example of what I want:

<div data-text="model:truncatedName 30" />

Here's is a test case for this issue:

it('should allow arguments for computed properties', function() {
  listItem.setAttribute('data-text', 'data:name foo bar');
  data.name = function() { return this.get('name') };
  spyOn(data, 'name');
  rivets.bind(el, bindData);
  expect(data.name).toHaveBeenCalledWith('foo', 'bar');
}); 

Iteration Binding

I see that we can currently iterate over groups of data using the data-html attribute and custom formatters, but I'm curious if the data-each binding is close to making it into the library? Seems like a bit of a pain to always have to write formatters when I want to iterate over something. Otherwise, great work thus far!

nested model support

Is it possible to get support for nested models so, story.category.name would update the model and yield { ..., 'category':{'name' : '...' }?

Iteration Binding needs to be more flexible with how it gets and iterates the collection

I'm building an app with Stapes.js + Rivets.js and ran into a problem when doing an iteration binding. Rivets doesn't use an adapter to retrieve the collection and assumes that the attribute it's iterating over is an array. Stapes has specific methods for dealing with collections, mainly getAllAsArray and each.

The issue is with this piece of code:

# Sets the value for the binding. This Basically just runs the binding routine
# with the suplied value formatted.
set: (value) =>
  # ... snip ...
  else if @options.special is "iteration"
    @routine @el, value, @ # <--- assumes value is an array :(

I got around it by defining a method on my view, and calling it from the binding. It's not the best solution because it adds complexity to the view and also requires manually updating the binding when items are added/removed from the collection. I will have to think about this problem more and post a followup comment with ideas but just wanted to shed some light on this issue to get a discussion going.

Vehicle = Stapes.create()
Vehicles = Stapes.create()

ListVehiclesView = Stapes.create().extend
  init: ->
    @set "vehicles", Vehicles
    @set "vehiclesCollection", @get("vehicles").getAllAsArray()
    @render()
  render: ->
    @view = rivets.bind $("#list-vehicles"), view: @

v = Vehicle.create(); v.set modelYear: 1980, make: "Ford"
v2 = Vehicle.create(); v2.set modelYear: 2012, make: "Chevy"
v3 = Vehicle.create(); v3.set modelYear: 1993, make: "Toyota"
Vehicles.push [v, v2, v3]

ListVehiclesView.init()
<div id="list-vehicles">
  <h2>List of Vehicles here</h2>
  <ul>
    <li data-each-vehicle="view.vehiclesCollection">
      <span data-text="vehicle.make"></span>
    </li>
  </ul>
</div>

Issue with unbinding callbacks on the model

Related to #15.

The callback being passed to the adapter's subscribe function is the Rivets.Binding.set function, but the actual callback that gets binded to the model inside the subscribe function may not necessarily be that exact callback function (this is the case with Backbone, because Backbone's event callbacks take the value as the second argument, not the first).

To illustrate where this is problematic, consider an adapter that has the following subscribe and unsubscribe functions:

subscribe: (obj, keypath, callback) ->
  obj.on "change:#{keypath}", callback

unsubscribe: (obj, keypath, callback) ->
  obj.off "change:#{keypath}", callback

This will subscribe and unsubscribe fine, because those callbacks are the same function (Rivets.Binding.set). In the case of Backbone though, the subscribe and unsubscribe functions would look like this.

subscribe: (obj, keypath, callback) ->
  obj.on "change:#{keypath}", (model, value) -> callback(value)

unsubscribe: (obj, keypath, callback) ->
  obj.off "change:#{keypath}", (model, value) -> callback(value)

This subscribes fine, but cannot unsubscribe since the function being passed to off is different function than the one passed into on. This problem is difficult because Rivets doesn't know anything about that (model, value) -> callback(value) function, since it's only available in the context of the adapter.

I'm hoping that there is a way to pass that function into Rivets without overcomplicating the adapter interface.

view.unbind() not working

Consider this example:

http://jsfiddle.net/leoasis/3XvTF/1/

Notice that first I'm binding the model to the div that contains the input, then unbinding it, and the binding the model to the div with the span.

However, if I change the text in the input, you can see that the model is being modified (by seeing the span that changes the text), meaning that the model is still bound to the input.

Wiki of example projects?

Are there any example projects that can be pointed to using Rails as a backend and using Backbone on the client?

Missing default for value routine

In Rivets.routines.value, no default string ('') is specified - unlike the HTML and Text binders. This is leading to my forms ending up with default values of undefined instead of a blank string. I'm happy to fix and test this in a pull request - I just wanted to confirm if it was intended behaviour first?

Add value as tag class

Would be really cool if you could include an additional arbitrary class based on an attribute value, for example:

Object `message` has attribute icon

<i class="icon" data-class-="message.icon">

With icon = "alert" you would have

<i class="icon alert" ...>

When it changes from "alert" to "warning"

<i class="icon warning" ...>

Right now I am getting around this by providing methods on the model that calculate the full class value and have it depend on the attribute that changes, but this feels a bit roundabout.

message.icon_class = function() { return "icon " + this.get('icon') }

<i data-class="message:icon_class < .icon">

Thoughts? I'm bit for the moment but I might look in to implementing if you think it would make a valuable feature.

Form validation

I know that you've stated in the description of this project and in a few other places that the aim of this project is supposed to be that it remains lightweight but there is a slight issue with form validation and integration with this plugin.

In order to use an already existing form validation plugin for Backbone (e.g. something like https://github.com/thedersen/backbone.validation) you would end up having to define two sets of selectors so that you could:

  1. Have valid form validations.
  2. Have your templates update with the standard rivets stuff

This makes for code duplication etc, which is really not very nice.

It would be good if either the scope of this project was expanded to account for form validation as well; or it could be better integrated into validation plugins like the aforementioned one, to save on repeating code etc.

Better documentation

Need to take the time to rewrite the current documentation with something a bit more structured and clear.

Formatters and computed properties

This doesn't work:

<div data-text="model:foo < name | bar baz"></div>

model.foo is never called and the first argument for the formatter is a function.

Improve data-value bindings on dynamic select elements

This isn't really a bug but more a feature request

Example:

<select data-value="model.item" >
  <option data-each-item="model.items" data-text="item.name" data-value="item.name"></option>
</select>

If you bind a model to this it won't select the proper item in the select because it will attempt to set the value before the options exist. Right now I'm working around it by manually forcing the data-value binding to run again.

Any ideas of how to make it better?

nested backbone-ish models

Hi,
I have a Backbone model where I call

this.model.set( {person: {name:"Chris", id:5}});

and I was wondering how I could get rivets to hook it up?

I tried

<span data-text="order.person.name"></span>
...
rivets.bind($(...), order:this.model)

but it doesn't work.

Thanks,
Chris

Binding to Backbone.Collection with the data-each- binding

I'm trying to bind to a Backbone Collection in my view using rivets and have been using the following code to ensure that they work in the same way as other models do:

var IterableCollection = {};
IterableCollection.prototype = {
    get: function(id){
        if(id === 'models'){
            return this.models;
        }
        return Backbone.Collection.prototype.get.call(this, id);
    },
    add: function(models, options){
        var ret = Backbone.Collection.prototype.add.apply(this, arguments);
        options || (options = {});
        this.trigger('change:models', this, this.models);
        return ret;
    },
    remove: function(models, options){
        var ret = Backbone.Collection.prototype.remove.apply(this, arguments);
        options || (options = {});
        this.trigger('change:models', this, this.models);
        return ret;
    },
    reset: function(models, options){
        var ret = Backbone.Collection.prototype.reset.apply(this, arguments);
        options || (options = {});
        this.trigger('change:models', this, this.models);
        return ret;
    }
};

Then I make sure my collection inherits from this:

_.extend(MyCollection.prototype, IterableCollection.prototype);

This has made it so that in the templates I can simply pass the collection to the rivets binding, and correctly bind to change events on the collection.

Is this the correct way that it should be implemented? It feels vaguely hackish..

My other point would be that it may be useful to have a wiki somewhere or something that we can use to put in useful snippets (perhaps like this one) that would be useful for people to use in conjunction with rivets for Backbone, or Spine or perhaps another framework?

Rivets iteration not working

It works the first time but all subsequent updates do nothing. I log from iterationBinding and can see the new data (I added one element to the array) being passed in from the subscriber. Am I writing my binding wrong?

<ul>
   <li data-each-bid="root.auction.bids">
      <span data-text="bid.name"></span>
   </li>
</ul>

Code Coloring

Ok, I'm just gonna be that guy....
Why not use

"code coloring" 

in your github markdown?

Issue parsing dependencies

This is after #78 has been applied (haven't tested if it is a bug with vanilla)

<div data-show=":​completed | length | overZero < .items">

yields

options = {
  bypass: true
  formatters: ["length", "overZero < .items"]
}

when it should be

options = {
  bypass: true
  formatters: ["length", "overZero"]
  dependencies: [".items"]
}

Iteration binding does not work in IE

In the following code snippet from rivets:

for item in collection
    data = {}
    data[n] = m for n, m of binding.view.models
    data[name] = item
    itemEl = el.cloneNode true
    previous = binding.iterated[binding.iterated.length - 1] or binding.marker
    binding.marker.parentNode.insertBefore itemEl, previous.nextSibling

    binding.iterated.push
      el: itemEl
      view: rivets.bind itemEl, data

the computation of "previous" is either a marker (DOM element) or binding.iterated[last], which is an object {el, view}. So, in case of the object, previous.nextSibling is undefined. So insertBefore(itemEl, underfined) fails on IE.

Support for IE8 and down

Methods like String::trim and Array::map doesn't exist until IE9, support for them should be added.

I recommend using a lib like underscore.js for that. The production size of it is less than 5kb.
Otherwise it's fairly simple to implement those methods yourself.

Let me know, I'd be happy to fix this.

Support for attributes

I'd like to be able to set attributes based on a value.

An example is binding the src of an img tag.

Proposed syntax: data-attr="src=imageSrc1 | imageSrc2, class=someAttribute"

Binding to radio buttons and drop down issues

Hi,

I've got a couple of issues with binding to radio buttons and select lists, both refer to the example below:

<input id="test-radio-0" name="test-radio" type="radio" value="male" data-value="user.gender">Male</input>
<input id="test-radio-1" name="test-radio" type="radio" value="female" data-value="user.gender">Female</input>

<select id="gender-selector">
    <option id="test-select-0" value="male" data-value="user.gender">Male</option>
    <option id="test-select-1" value="female" data-value="user.gender">Female</option>
</select>

Initial values aren't getting set for either control type, and subsequently binding is only working for the radio button example. I'm using Backbone, and initial values and two way binding are working fine with text inputs elsewhere in the form. Any idea what could be causing this?

Allow relations between keys

Sometimes my models have a helper function that uses an attribute to build a string.

For example I could have a helper method that generates a URL which depends on an attribute of the model.
It would be nice if I could use rivets to bind the change of one attribute to something else.

This is in no way a proposed syntax, this is just so you can get an idea of what I mean.

The view:

<!-- When `model.foo` changes the href is changed to `model.bar`
<a id="search-link" data-href="model.foo > model.bar">Search</a>

The JS (not including the adapter):

Model = Backbone.Model.extend({
  bar: function() { 
    return "https://www.google.com/search?q=" + this.foo;
  }
});

model = new Model({
  foo: 'some value',
});

# The a tag's href now becomes a search for `some value`
rivets.bind(document.getElementById('search-link'), {model: model});

# The a tag's href now becomes a search for `some other value`
model.set('some other value');

Another syntax would be model.foo#bar, you get the idea :)

The whole function calling thing will be taken care of by the adapter.

Computed properties with dependent properties from another model

It would be nice if computed properties could depend on properties from another model. For example, if I wanted to show some fields if a person is aged 16 or over:

<div data-show="view:shouldShowDrivingQuestions < person.age">
  <ul>
    <li>When did you get your drivers license?
    <li>Have you ever gotten a ticket?
  </ul>
</div>

Binding to radio buttons

I think I'm just doing something wrong, but I'm having trouble binding to and from a radio button group. Given HTML like the following:

<input data-value="purchase.amount" name="amount" type="radio" value="500">$5</input>
<input data-value="purchase.amount" name="amount" type="radio" value="1000">$10</input>
<input data-value="purchase.amount" name="amount" type="radio" value="1500">$15</input>
<input data-value="purchase.amount" name="amount" type="radio" value="2000">$20</input>

the Purchase model's amount just gets set to "true" if any of the radio buttons are pressed and false otherwise. I've also tried data-checked, but it works the same way in this case.

I'd like to have a bidirectional relationship on radio buttons so that the correct input is selected based on amount, and the amount is updated based on selection.

Do you have an example that involves radio buttons? If you can get me started, I'll write it up and contribute it back to the README.

Thanks!

Validation binding breaks Rivets binding?

Wondering if anyone else has encountered this:

We're using Rivets and Backbone.Validation for validating forms/models. Rivets binding is working fine until Backbone.Validation.bind(view) is called, at which point the validation binding works fine but attributes are no longer being updated. Any suggestions/directions I should be looking into? Thanks!

Parser methods

I have a field on my model that holds a date in ISO string format (i.e. 2012-10-08T00:00:00Z). When I'm rendering this value into a text input I use the following formatter to output it in DD/MM/YYYY format:

rivets.formatters.date = function(value) {
  return moment(value).format('DD/MM/YYYY');
};

When the value is changed I need to store the updated value back into the model as an ISO string. I can't see an easy way to do this without checking the keypath value passed to my adapter's publish method to check whether the field i'm interested in is being changed (which obviously isn't particularly generic).

Ideally it'd be nice to be able to specify a set of parsers to be used in conjunction with the formatters, e.g.:

rivets.parsers.date = function(value) {
  return moment(value + '+0000', 'DD/MM/YYYYZZ').utc().format()
};

I can see two ways to hook this into the existing code:

  1. Pass the binding to the adapter's publish method along with the other attributes. This would be a small change to the rivets codebase, but would expose an easy way to see which formatters are attached to the current binding. I could then simply call a parser with the same name from within my codebase.
  2. Add a parseValue method to Rivets.Binding which is called before the adapter's publish method and uses the list of formatter names on the current binding to call parser methods in a rivets.parsers object.

I'm happy to create a pull request for either of these approaches, just wanted to check if there is any interest before I do? Is there a simpler way to achieve what I'm after?

Segregated configuration/formatters

I think being able to set a default global config + being able to pass a config into rivets.bind would make it a lot easier to use different types of data. If I have a backbone model with an adapter for it and a stapes model with its own adapter I should be able to pass it into rivets.bind vs. having to define one global adapter that tries to figure out what type of model it is and act accordingly

Add tests

It's quite difficult to help write code for this if there aren't any tests.
The tests would be fairly simple to write as well.

Let me know if you want me to start on some tests.

Binding arrays

Is it possible to bind a specific index of an array? How about iterating a subset of an array with a foreach?

data-on-[event] is weird

I noticed this last week when working with the event binding

When using the example <button data-on-click="item.destroy">remove</button> what I believe is happening is

var func = item.get('destroy');
elm.on('click', func);

It is odd that the function is expected to be an attribute(because its using the . syntax), and when it is called this is set to the html element, as the function itself is being bound to the event. What I would expect is more like

var func = function() { item.destroy() };
elm.on('click', func);

Thoughts?

Conditional Logic

It's me again :)

I'm new to data binding but i've been checking out knockout as well.

is it possible to do conditional logic with rivets?

something like this

<div class="post">
    <div data-if="post.type = foo">
        <h2>Foo</h2>
        <span>foo stuff</span>
    </div>
    <div data-if="post.type = bar">
        <h2>Bar</h2>
        <span>bar stuff</span>
    </div>
</div>

More semantic syntax for computed properties with dependent properties

Instead of >, I was thinking maybe it would make more sense to use a < or = to indicate dependent properties:

<span data-text="event:duration < start end"></span>
or
<span data-text="event:duration = start end"></span>

In POSIX shells, < is used for redirecting input. In the context of dependent properties, it feels more natural to think of the items on the right side as providing input for the computed property.

When you use >, it feels unnatural, like the items on the right side are dependent on the computed property on the left.

Another possibility is using = to symbolize that the computed property on the left is "equal" to the combination of dependent properties on the right.

Binding with data-html causes a length variation when looping over nodes.

If there is html already at the keypath and it contains multiple html tags, it will build those elements immediately (as bind-html is supposed to) which causes the length of the nodes in that scope to increase by n, leaving the full iteration n nodes too short (the last few element may not get binded).

rivets.formatters don't work within a data-each

I'm not able to run a rivets formatter within a data-each construct. I can hit the formatter fine outside of the data-each, just not inside.

     html:
                     <li data-each-item="store:Items">
            <span data-text="item | info"></span>
        </li>

    js:
    rivets.formatters.info = function(value){
         return value.GetText(); // a method to format the item's text
    }

var Store = Backbone.Model.extend({
    initialize: function(options) {
        this.Title = "Cyclist Stuff";
        this.Items = new ItemCollection(); // Backbone Collection of Simple Items (Backbone Models)
        this.Items.reset(options.Items);
    }
});

Suggestions for lightweight observables library

I'm looking for a lightweight observables library to use with Rivets' adapter to get/set values on an object and observe changes, ideally with keypath support (e.g. obj.set("some.deep.key", value)) and mixins (e.g. obj.mixin({ some: "value" })). Any suggestions?

Uncaught SyntaxError: Invalid regular expression: /^on-.+$/: Stack overflow

Uncaught SyntaxError: Invalid regular expression: /^on-.+$/: Stack overflow
Rivets.View.View.build.parseNode
Rivets.View.View.build
__bind
View
rivets.bind
Rivets.binders.each-*.routine
Rivets.Binding.Binding.set
__bind
Rivets.Binding.Binding.sync
__bind
Rivets.Binding.Binding.bind
__bind
Rivets.View.View.bind
__bind
rivets.bind
Rivets.binders.each-*.routine
Rivets.Binding.Binding.set
__bind
Rivets.Binding.Binding.sync
__bind
Rivets.Binding.Binding.bind
__bind
Rivets.View.View.bind
__bind
rivets.bind
Rivets.binders.each-*.routine
Rivets.Binding.Binding.set
__bind
Rivets.Binding.Binding.sync
__bind
Rivets.Binding.Binding.bind
__bind
Rivets.View.View.bind
__bind
rivets.bind
Rivets.binders.each-*.routine

(repeats like this for a long time)

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.