Unique and efficient front-end framework for PHP
mounting
hooklazy
gropingFull Changelog: https://github.com/viewi/viewi/compare/v2.3.1...v2.3.2
Full Changelog: https://github.com/viewi/viewi/compare/v2.3.0...v2.3.1
IViewiBridge::request
has changed to allow linking with the parent context:function request(Request $request): mixed; // old
function request(Request $request, Engine $currentEngine): mixed; // new
Response
object as a parameter:class ContentPage extends BaseComponent
{
use HasLocalization;
public string $title = 'Loading..';
public ?PageModel $page = null;
public bool $notFound = false;
public function __construct(private HttpClient $http, private ClientRoute $route)
{
}
public function init()
{
$this->http->get("/api/content?path={$this->route->getUrlPath()}")
->then(function (?PageModel $page) {
$this->page = $page;
$this->title = $this->page->MetaTitle ? $this->page->MetaTitle : $this->page->Title;
}, function (Response $response) {
if ($response && $response->status) {
if ($response->status === 404) {
$this->notFound = true;
$this->title = $this->localization->t('layout.page-not-found');
$this->route->setResponseStatus($response->status);
}
}
});
}
}
Engine
set
and getIfExists
methods for more convenient integration:getIfExists(string $name);
set(string $name, $mixed);
Subscriber
- consume/provide data in publish/subscribe manner:#[Singleton]
class AuthService
{
private Subscriber $userSubscriber;
public function usage()
{
// create
$this->userSubscriber = new Subscriber();
// pusblish value
$this->userSubscriber->publish($this->userSession);
// subscribe
$subscription = $this->userSubscriber->subscribe(function (UserSession $user) { /** use it **/});
// unsubscribe
$this->userSubscriber->unsubscribe($subscription);
}
ClientRoute::urlWatcher
- watch URL change and get notified: public function __construct(private ClientRoute $route)
{
}
public function init()
{
$this->pathSubscription = $this->route->urlWatcher()->subscribe(function (string $urlPath) {
$this->activeLink = $urlPath;
});
$user?->Name
converts to user?.Name
--$i
, ++$i
support for JavaScript transpiler.private array $list = /* @jsobject */ [];
converts to list = {};
class ValidationMessage extends BaseComponent
{
publis string $error = '';
}
Order details:
...
Errors:
<ValidationMessage #validationMessages />
class OrderEdit extends BaseComponent
{
public ?ValidationMessage $validationMessages = null;
public function validate()
{
// ...
$this->validationMessages->error = 'Something happend';
Provide a new instance with #[Inject(Scope::COMPONENT)]
:
class ActionForm extends BaseComponent
{
public function __construct(
#[Inject(Scope::COMPONENT)]
private FormContext $form
) {
}
Use provided instance with #[Inject(Scope::PARENT)]
:
class TextInput extends BaseComponent
{
public function __construct(
#[Inject(Scope::PARENT)]
private ?FormContext $form = null
) {
}
Alternative usage, provide with $this->provide
:
class ActionForm extends BaseComponent
{
public function init()
{
$this->formContext = new FormContext();
$this->provide(ActionForm::class, $this->formContext);
}
Use provided instance with $this->inject
:
class TextInput extends BaseComponent
{
public function init()
{
/**
* @var ?FormContext $formContext
*/
$formContext = $this->inject(ActionForm::class);
}
preg_match
JavaScript version.foreach
directive.array_filter
JavaScript version.ClientRoute::setResponseStatus
to provide the response status code at runtime.IStartUp
:#[Singleton]
class Localization implements IStartUp
{
private array $resources = /* @jsobject */ [];
public function __construct(private HttpClient $http)
{
}
// execute the action on application start up
public function setUp()
{
$this->http->get("/api/locale-resource/1")
->then(function ($resources) {
$this->resources = $resources;
}, function () {
// error
});
}
#[GlobalEntry]
:Example:
#[Singleton]
class Localization implements IStartUp
{
private array $resources = /* @jsobject */ [];
#[GlobalEntry]
public function t(string $key, ?array $params = null)
{
$text = $this->resources[$key] ?? $key;
if ($params !== null) {
foreach ($params as $key => $value) {
$text = str_replace("{{$key}}", $value, $text);
}
}
return $text;
}
}
Use it anywhere in the template
<h1>
{t('email.layout.title')}
</h1>
#[Inject(Scope)]
:class MyPage
{
#[Inject(Scope::SINGLETON)]
public Localization $localization;
}
trait HasLocalization
{
#[Inject(Scope::SINGLETON)]
public Localization $localization;
}
class ContentPage extends BaseComponent
{
use HasLocalization;
public function init()
{
//...
if ($pageNotFound) {
$this->title = $this->localization->t('layout.page-not-found');
}
}
}
Full Changelog: https://github.com/viewi/viewi/compare/v2.2.4...v2.3.0
foreach
order mismatchFull Changelog: https://github.com/viewi/viewi/compare/v2.2.3...v2.2.4
Full Changelog: https://github.com/viewi/viewi/compare/v2.2.2...v2.2.3
<svg class="bi pe-none " width="16" height="16">
<use xlink:href="#home" />
</svg>
Full Changelog: https://github.com/viewi/viewi/compare/v2.2.1...v2.2.2
-a
argumentFull Changelog: https://github.com/viewi/viewi/compare/v2.2.0...v2.2.1
Bug fix release
Full Changelog: https://github.com/viewi/viewi/compare/v2.1.1...v2.2.0
Full Changelog: https://github.com/viewi/viewi/compare/v2.1.0...v2.1.1
Bug fix release with improvements for NPM watch and build.
Full Changelog: https://github.com/viewi/viewi/compare/v2.0.3...v2.1.0