Chandra
view release on metacpan or search on metacpan
lib/Chandra/Component.pm view on Meta::CPAN
# ââ Lifecycle ââââââââââââââââââââââââââââââââââââââââââââââ
sub mount {
my ($self, $app, $selector) = @_;
comp__app $self, $app;
comp__selector $self, $selector;
# Bind the action dispatcher for this component
my $cid = $self->_ensure_cid;
$app->bind("_comp_action_$cid", sub {
my ($action, @args) = @_;
my $method = "on_$action";
if ($self->can($method)) {
return $self->$method(@args);
}
warn "Chandra::Component: no handler '$method' on " . ref($self) . "\n";
return undef;
});
# Render and inject
my $html = $self->_wrap_render;
$app->update($selector, $html);
comp__mounted $self, 1;
$self->on_mount if $self->can('on_mount');
# Mount children
for my $child (@{comp__children $self}) {
my $child_cid = $child->_cid;
$child->mount($app, "#$child_cid");
}
return $self;
}
sub unmount {
my ($self) = @_;
my $app = comp__app $self;
# Unmount children first
for my $child (@{comp__children $self}) {
$child->unmount;
}
$self->on_unmount if $self->can('on_unmount');
# Remove from DOM
my $cid = $self->_ensure_cid;
if ($app) {
$app->eval("var _e=document.getElementById('$cid');if(_e)_e.remove();");
}
comp__mounted $self, 0;
comp__app $self, undef;
delete $_comp_registry{$cid};
return $self;
}
sub update {
my ($self) = @_;
my $app = comp__app $self;
return $self unless $app && comp__mounted $self;
my $cid = $self->_ensure_cid;
my $inner = $self->_render_inner;
$app->update("#$cid", $inner);
$self->on_update if $self->can('on_update');
return $self;
}
# Update only a sub-section of the component by a stable CSS selector.
# The selector is relative to the component's root element.
# The $html should already have actions rewritten.
sub update_part {
my ($self, $sub_id, $html) = @_;
my $app = comp__app $self;
return $self unless $app && comp__mounted $self;
$html = $self->_rewrite_actions($html);
$app->update("#$sub_id", $html);
return $self;
}
# ââ Child composition ââââââââââââââââââââââââââââââââââââââ
sub child {
my ($self, $component) = @_;
comp__parent $component, $self;
my $children = comp__children $self;
push @$children, $component;
comp__children $self, $children;
return $self;
}
sub render_children {
my ($self) = @_;
my $html = '';
for my $child (@{comp__children $self}) {
$html .= $child->_wrap_render;
}
return $html;
}
# ââ Internal âââââââââââââââââââââââââââââââââââââââââââââââ
my %_INPUT_TAGS = map { $_ => 1 } qw(input select textarea);
sub _ensure_cid {
my ($self) = @_;
my $cid = comp__cid $self;
unless ($cid) {
$cid = '_comp_' . ++$_comp_id_counter;
comp__cid $self, $cid;
$_comp_registry{$cid} = $self;
}
return $cid;
}
sub _action_to_js {
my ($self, $tag, $action) = @_;
my $cid = $self->_ensure_cid;
my ($method, @params) = split /:/, $action;
my $args = Cpanel::JSON::XS::encode_json([$method, @params]);
$args =~ s/"/"/g;
my $invoke = "window.chandra.invoke("_comp_action_$cid"";
if ($_INPUT_TAGS{$tag}) {
if ($tag eq 'select') {
# Selects fire immediately on change
return ('onchange',
"var _a=${args};_a.push(this.value);${invoke},_a)");
}
( run in 1.917 second using v1.01-cache-2.11-cpan-13bb782fe5a )