AngularJS & Dancer

for Modern Web Development

Otherwise titled:

How I tried to get my app ready for Dancer, instead of just using Dancer, and how that worked out for me.

Josh Lavin

@jdigory

IRC: digory

Migrate

How?

  • Legacy App paradigm
  • Modern Perl
  • Organize business logic

Move already


@_TOP_@

[scratch page_title]

[perl] my $has_course; for (grep {$_->{mv_ib} eq 'course'} @$Items) { $has_course++; } return $has_course ? '

You have a course!

' : ''; [/perl] @_BOTTOM_@

Separation of Concerns

HTML + placeholders?


@_TOP_@
[my-tag-attr-list 
    page_title="[scratch page_title]"
    has_course="[perl] ... [/perl]"
    buy_phrase="Buy [if cgi items]more[else]now[/else][/if]"
]

    

{PAGE_TITLE}

{HAS_COURSE?}

You have a course!

{/HAS_COURSE?} [/my-tag-attr-list] @_BOTTOM_@

JavaScript framework
Front-end separated from back-end
Eats JSON


@_TOP_@

You have a course!

@_BOTTOM_@

<html ng-app="MyApp">
...


</html>
					

package MyApp::Feedback;
use MyApp;
my $app = MyApp->new( ... );
sub list {
    my $self = shift;
    my $code = shift
        or return $app->die('Need code');
    my $rows = $app->dbh($feedback_table)->...;
    return $rows;
}					
					

package MyApp::Feedback;
use Moo;
with MyApp::HasDatabase;
sub list {
  my $self = shift;
  my $code = shift
    or die 'Need code';
  my $rows = $self->dbh->...;
  return $rows;
}					
					

Before:


sub _route_feedback {
    my $self = shift;
    my (undef, $sub_action, $code) = split '/', $self->route;
    $code ||= $sub_action;
    $self->_set_status('400 Bad Request');   # start with 400
    my $feedback = MyApp::Feedback->new;
    for ($sub_action) {
        when ("list") {
            my $feedbacks = $feedback->list($code);
            $self->_set_tmp( to_json($feedbacks) );
            $self->_set_path('special/json');
            $self->_set_content_type('application/json; charset=UTF-8');
            $self->_set_status('200 OK') if $feedbacks;
        }
        default {  #...
					

Before, continued:


default {
    for ($self->method) {
        when ('GET') {
            my $row = $feedback->get($code)
                or return $self->_route_error;
            $self->_set_tmp( to_json($row) );
            $self->_set_path('special/json');
            $self->_set_content_type('application/json; charset=UTF-8');
            $self->_set_status('200 OK') if $row;
        }
        when ('POST') {
            my $params = $self->body_parameters
                or return $self->_route_error;
            $params = from_json($params);
            my $result = $feedback->save($params);
            $self->_set_status('200 OK') if $result;
            $self->_set_path('special/json');
            $self->_set_content_type('application/json; charset=UTF-8');
        }
    }
					

After:


prefix '/feedback' => sub {
    my $feedback = MyApp::Feedback->new;
    get '/list/:id' => sub {
    	return $feedback->list( param 'id' );
    };
    get '/:code' => sub {
        return $feedback->get( param 'code' );
    };
    post '' => sub {
        return $feedback->save( scalar params );
    };
};
					

TMTOWTDI

  • Have Dancer deliver HTML files (AngularJS)
  • Or have web server deliver them
    • especially if SPA
    • JSON Web Tokens (JWT)

Now starring Dancer

Dancer is better?

Dancer is better:

Routes contain code specific to the Web.

Routes call non-Dancer modules (business logic).

Return the data in the appropriate format.

Lessons learned

Separate concerns

Keep it testable

Just start somewhere

The End Beginning