Skip to content

An opal-vienna rewrite with Convention over Configuration in mind

License

Notifications You must be signed in to change notification settings

unplugandplay/opal-praha

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Praha: Client side MVC framework for Opal

Next step: the release of THE TodoMVC implementation.

Installation

Add opal-praha to your Gemfile with a reference to the Github source.

gem 'opal-praha'

If you're compiling opal in a static application, make sure to require bundler first.

require 'bundler'
Bundler.require

Model

Client side models.

class Book < Praha::Model
  attributes :title, :author
end

book = Book.new(title: 'My awesome book', author: 'Bob')
book.title = 'Bob: A story of awesome'

Attributes

Attributes can be defined on subclasses using attributes. This simply defines a getter/setter method using attr_accessor. You can override either method as expected:

class Book < Praha::Model
  attributes :title, :release_date

  # If date is a string, then we need to parse it
  def release_date=(date)
    date = Date.parse(date) if String === date
    @release_date = date
  end
end

book = Book.new(:release_date => '2013-1-10')
book.release_date
# => #<Date: 2013-1-10>

Views

Praha::View is a simple wrapper class around a dom element representing a view of some model (or models). A view's element is dynamically created when first accessed. View.element can be used to specify a dom selector to find the view in the dom.

Assuming the given html:

<body>
  <div id="foo">
    <span>Hi</span>
  </div>
</body>

We can create our view like so:

class MyView < Praha::View
  element '#foo'
end

MyView.new.element
# => #<Element: [<div id="foo">]>

A real, existing, element can also be passed into the class method:

class MyView < Praha::View
  # Instances of this view will have the document as an element
  element Document
end

Views can have parents. If a child view is created, then the dom selector is only searched inside the parents element.

Customizing elements

A View will render as a div tag, by default, with no classes (unless an element selector is defined). Both these can be overriden inside your view subclass.

class NavigationView < Praha::View
  def tag_name
    :ul
  end

  def class_name
    "navbar navbar-blue"
  end
end

Rendering views

Views have a placeholder render method, that doesnt do anything by default. This is the place to put rendering logic.

class MyView < Praha::View
  def render
    element.html = 'Welcome to my rubyicious page'
  end
end

view = MyView.new
view.render

view.element
# => '<div>Welcome to my rubyicious page</div>'

Listening for events

When an element is created, defined events can be added to it. When a view is destroyed, these event handlers are then removed.

In Praha, event listening is done directly in the HTML or HAML

.view
  %input.toggle(opal-click){type: "checkbox", checked: @todo.completed}
  %label.editable(opal-dblclick)= @todo.title
  %button.destroy(opal-click)
.form
  %input.edit(opal-focusout="finish_editing" opal-press-enter="finish_editing"){value: @todo.title}

Customizing element creation

You can also override create_element if you wish to have any custom element creation behaviour.

For example, a subview that is created from a parent element

class NavigationView < Praha::View
  def initialize(parent, selector)
    @parent, @selector = parent, selector
  end

  def create_element
    @parent.find(@selector)
  end
end

Assuming we have the html:

<div id="header">
  <img id="logo" src="logo.png" />
  <ul class="navigation">
    <li>Homepage</li>
  </ul>
</div>

We can use the navigation view like this:

@header = Element.find '#header'
nav_view = NavigationView.new @header, '.navigation'

nav_view.element
# => [<ul class="navigation">]

Router

Praha::Router is a simple router that watches for hashchange events.

router = Praha::Router.new

router.route("/users") do
  puts "need to show all users"
end

router.route("/users/:id") do |params|
  puts "need to show user: #{ params[:id] }"
end


# visit "example.com/#/users"
# visit "example.com/#/users/3"
# visit "example.com/#/users/5"

# => "need to show all users"
# => need to show user: 3
# => need to show user: 5

Observable

Adds KVO style attribute observing.

class MyObject
  include Praha::Observable

  attr_accessor :name
  attr_reader :age

  def age=(age)
    @age = age + 10
  end
end

obj = MyObject.new
obj.add_observer(:name) { |new_val| puts "name changed to #{new_val}" }
obj.add_observer(:age) { |new_age| puts "age changed to #{new_age}" }

obj.name = "bob"
obj.age = 42

# => "name changed to bob"
# => "age changed to 52"

Observable Arrays

class MyArray
  include Praha::ObservableArray
end

array = MyArray.new

array.add_observer(:content) { |content| puts "content is now #{content}" }
array.add_observer(:size) { |size| puts "size is now #{size}" }

array << :foo
array << :bar

# => content is now [:foo]
# => size is now 1
# => content is now [:bar]
# => size is now 2

Todo

  • Support older browsers which do not support onhashchange.
  • Support not-hash style routes with HTML5 routing.

License

MIT

About

An opal-vienna rewrite with Convention over Configuration in mind

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages