(before demo begins:

bin/rails g reactrb:component Foo (to preload bin/rails)

then remove all components, Todo controller, and put model back into top level directory, remove sync macros, and remove routes, close all editor tabs

then run rspec spec/components/footer_link_spec.rb so its in the command buffer

rails new .

for this demo we are have some predefined specs defined, and we will use the standard Todo example style sheet…

cp -r ../TodoTests .

move the .css file to where it belongs

edit Gemfile, add gem ‘reactive_rails_generator’ and all the stuff from spec/Gemfile

bundle install
rails g reactrb:install --all
bundle update

now we have our basic rails app plus everything we need for react.rb

lets add a simple todo model, where each todo has a title, and a completed flag...

bundle exec rails g model todo title:string completed:boolean

edit the migration and add: “, null: false, default: false”

bundle exec rake db:migrate
bundle exec rake db:test:prepare

 

move the todo to public (explain why), open and add scopes (scope :completed tab)

 

 

talk about FooterLink, then run

bundle exec rails g reactrb:component FooterLink
we will pass 2 parameters to our FooterLink:  the scope (:all, :completed, or :active) that this link is displaying, the current scope that is being used (so we can highlight it)

add params

 def render

   a(:class) tab (remove ending parens)

 end

explain how we use standard rspec, capybara and some react helpers to run our tests, have a look at footer_link_spec, then run

DRIVER=pg bundle exec rspec spec/components/footer_link_spec.rb

Now that we have a footer link lets build out the Footer component

bundle exec rails g reactrb:component Footer

the footer will take two params:  the number “incomplete_count” (an integer) and the current_scope

add params (param :current_scope tab)

add render

       def render

         footer tab

       end

notice how the param current_scope is getting passed from higher level components down through the system.   React will determine what needs to be re-rendered as the value of the params change.

DRIVER=ff bundle exec rspec spec/components/footer_spec.rb

Now we need an element to enter a new title and if we do it right we can reuse it to edit the title.

bundle exec rails g reactrb:component EditItem

param todo tab

discuss resulting code

DRIVER=ff bundle exec rspec spec/components/edit_item_spec.rb

open up firebug, and demonstrate saving
binding.pry> quit

so we need one more component and then we can put it all together… as we can see the display consists of a list of todos, so lets build a component that displays a single todo as list item (or li) tag.  Lets call our component TodoItem

bundle exec rails g reactrb:component TodoItem

we know its going to take one param (just like EditItem) of type Todo.

        param :todo, type: Todo

and we can start with a very simple render method that shows the todo title in a label,

we will tag the li with the todo_item class

       def render

         li.todo_item do

           label { todo.title }

         end

        end

______________________________
OPTIONAL:
DRIVER=ff bundle exec rspec spec/components/todo_item_spec.rb

now each todo should have a checkbox to the left, that indicates whether the todo is completed or not. 

   input.toggle(type: :checkbox, (params.todo.completed ? :checked : :unchecked) => true).

   on(:click) do

     params.todo.completed = !params.todo.completed

     params.todo.save

   end

 

 

 

[OPTIONAL: ...]

DRIVER=ff bundle exec rspec spec/components/todo_item_spec.rb

the next thing we need to add is a delete link.

    a.destroy.on(:click) { params.todo.destroy }

 

 

 

[OPTIONAL: ...]

DRIVER=ff bundle exec rspec spec/components/todo_item_spec.rb

Finally we also want to edit the title using the EditTitle component, when the user double clicks on the title.

This means that the component will have to be in 1 of 2 states:  Either displaying the component or editing the component.  

So lets add a react “state variable” to our component and use it to determine whether we display the component, or use the EditItem component.

Now we want to switch modes when the user doubleClick s on the title:  Each state has a “bang” method that is used to update the state, and trigger any necessary re-renders.

(add doubleClick handler to label)

so when we double click the component it switches to edit mode, and we just need to switch back once the user saves their changes:

         on(:save) { state.editing! false }.

         on(:cancel) { state.editing! false }

DRIVER=ff bundle exec rspec spec/components/todo_item_spec.rb

Our last component will be the top level “App”.

In keeping with Rails terminology we will call it Index.

bundle exec rails g reactrb:component Index

we will need a controller

bundle exec rails g controller Todos index 

update routes: get 'todos' => 'todos#index'

update the controller render_component

try it out:

DRIVER=ff bundle exec rescue rspec spec/integration/index_spec.rb

(we will use rescue to see what we need to do)

we see the component exists…

first what it should do is display the components in a list, we will pick up the styles with the todo-app and todo-list classes

 

     section.todo_app do

       ul.todo_list do

         Todo.all.each do |todo|

           TodoItem todo: todo

         end

       end

 

DRIVER=ff bundle exec rescue rspec spec/integration/index_spec.rb

 

now we need a way to add a new todo… hmmm… a new_todo lets make it a state variable that will hold the new_todo.  When the EditItem component saves it, we will create another new todo...

   define_state :new_todo

 

   before_mount do

     state.new_todo! Todo.new

   end

       header.header do

         h1 {"todos"}

         EditItem(todo: state.new_todo).

         on(:save) { state.new_todo! Todo.new(completed: false) }

       end

DRIVER=ff bundle exec rescue rspec spec/integration/index_spec.rb

okay lets add our footer component at the bottom:

           Footer(scope: :all, incomplete_count: Todo.active.count)

DRIVER=ff bundle exec rescue rspec spec/integration/index_spec.rb

cool that works…

 

The last thing we need to do is wire up the links (all, completed, active) in the footer.  Right now they do nothing… In a real app we would use a router component (like reactive-router) that tracks link transitions, updates the URL, and uses HTML 5 history to allow uses to have the back and forward button.

We will just use a very simple implementation that demonstrates react’s flux programming pattern…

First we will “export” a state variable called scope from the top level index component.  This will make it available as a class method outside of Index.  We will initialize it to :all in the before_mount callback, and then use its value in place of “:all” within index.

Finally we will update scope from our FooterLink component, when the user clicks on a link.

Everything basically works now, but there are few odds an ends to make it perfect…

 

   to_sync(:completed) { completed }

   to_sync(:active) { !completed }

 

live-demo-notes

By Mitch VanDuyn

live-demo-notes

Live demo notes

  • 796
Loading comments...

More from Mitch VanDuyn