{"id":15016852,"url":"https://github.com/clickfwd/yoyo","last_synced_at":"2026-04-11T03:30:22.757Z","repository":{"id":44589450,"uuid":"299350873","full_name":"clickfwd/yoyo","owner":"clickfwd","description":"Yoyo is a full-stack PHP framework to create rich, dynamic interfaces using server-rendered HTML. You keep on writing PHP and let Yoyo make your creations come alive.","archived":false,"fork":false,"pushed_at":"2025-02-11T14:02:00.000Z","size":391,"stargazers_count":204,"open_issues_count":0,"forks_count":18,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-15T15:04:09.841Z","etag":null,"topics":["blade","livewire","reactive","reactive-components","twig"],"latest_commit_sha":null,"homepage":"https://getyoyo.dev","language":"PHP","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clickfwd.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-09-28T15:23:48.000Z","updated_at":"2025-05-15T08:46:38.000Z","dependencies_parsed_at":"2024-04-16T14:37:12.575Z","dependency_job_id":"65de3a55-209c-4536-8e8c-973cea72016b","html_url":"https://github.com/clickfwd/yoyo","commit_stats":{"total_commits":304,"total_committers":5,"mean_commits":60.8,"dds":0.09539473684210531,"last_synced_commit":"7ec0579b56ab9915bb73080c403beb50e361bf09"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clickfwd%2Fyoyo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clickfwd%2Fyoyo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clickfwd%2Fyoyo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clickfwd%2Fyoyo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clickfwd","download_url":"https://codeload.github.com/clickfwd/yoyo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254364270,"owners_count":22058878,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blade","livewire","reactive","reactive-components","twig"],"created_at":"2024-09-24T19:49:28.646Z","updated_at":"2026-04-11T03:30:22.693Z","avatar_url":"https://github.com/clickfwd.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Yoyo\n\nYoyo is a full-stack PHP framework that you can use on any project to create rich dynamic interfaces using server-rendered HTML.\n\nWith Yoyo, you create reactive components that are seamlessly updated without the need to write any Javascript code.\n\nYoyo ships with a simple templating system, and  offers out-of-the-box support for [Blade](https://laravel.com/docs/8.x/blade), without having to use Laravel, and [Twig](https://twig.symfony.com/).\n\nInspired by [Laravel Livewire](https://laravel-livewire.com/) and [Sprig](https://putyourlightson.com/plugins/sprig), and using [htmx](https://htmx.org/).\n\n## 🚀 Yoyo Demo Apps  \n\nCheck out the [Yoyo Demo App](https://app.getyoyo.dev) to get a better idea of what you can build with Yoyo. It showcases many different types of Yoyo components. You can also clone and install the demo apps:\n\n- [Yoyo Blade App](https://github.com/clickfwd/yoyo-blade-app)\n- [Yoyo Laravel App](https://github.com/clickfwd/yoyo-laravel-app)\n- [Yoyo PHP template App](https://github.com/clickfwd/yoyo-app)\n- [Yoyo Twig App](https://github.com/clickfwd/yoyo-twig-app)\n\n## Documentation \n\n- [How it Works](#how-it-works)\n- [Installation](#installation)\n- [Updating](#updating)\n- [Configuring Yoyo](#configuring-yoyo)\n- [Creating Components](#creating-components)\n- [Rendering Components](#rendering-components)\n- [Properties](#properties)\n- [Actions](#actions)\n- [View Data](#view-data)\n- [Computed Properties](#computed-properties)\n- [Events](#events)\n- [Redirecting](#redirecting)\n- [Component Props](#component-props)\n- [Query String](#query-string)\n- [Loading States](#loading-states)\n- [Using Blade](#using-blade)\n- [Using Twig](#using-twig)\n- [License](#license)\n\n## How it Works\n\nYoyo components are rendered on page load and can be individually updated, without the need for page-reloads, based on user interaction and specific events.\n\nComponent update requests are sent directly to a Yoyo-designated route, where it processes the request and then sends the updated component HTML partial back to the browser.\n\nYoyo can update the browser URL state and trigger browser events straight from the server.\n\nBelow you can see what a Counter component looks like:\n\n**Component class**\n\n```php\n# /app/Yoyo/Counter.php\n\n\u003c?php \nnamespace App\\Yoyo;\n\nuse Clickfwd\\Yoyo\\Component;\n\nclass Counter extends Component\n{\n\tpublic $count = 0;\n\t\n\tprotected $props = ['count'];\n\n    public function increment()\n    {\n        $this-\u003ecount++;\n    }\n}\n```\n\n**Component template**\n\n```html\n\u003c!-- /app/resources/views/yoyo/counter.php --\u003e\n\n\u003cdiv\u003e\n\n\t\u003cbutton yoyo:get=\"increment\"\u003e+\u003c/button\u003e\n\t\n\t\u003cspan\u003e\u003c?php echo $count; ?\u003e\u003c/span\u003e\n\n\u003c/div\u003e\n```\n\nYes, it's that simple! One thing to note above is the use of the protected property `$props`. This indicates to Yoyo that the `count` variable, which is not explicitly available within the template, should be persisted and updated in every request.\n\n\n## Installation\n\n### Install the Package\n\n```bash\ncomposer require clickfwd/yoyo\n```\n\n#### Phalcon Framework Installation\n\nFor phalcon, you need to add di\n\n```php\n$di-\u003eregister(new \\Clickfwd\\Yoyo\\YoyoPhalconServiceProvider());\t\n```\n\nand you need to add router:\n\n```php\n$router-\u003eadd('/yoyo', [\n            'controller' =\u003e 'yoyo',\n            'action' =\u003e 'handle',\n        ]);\n```\n\nand you should create a controller and inherit from `Clickfwd\\Yoyo\\PhalconController` class.\n\n\n## Updating\n\nAfter performing the usual `composer update`, remember to also update the `yoyo.js` script per the [Load Assets](#load-assets) instructions.\n\n## Configuring Yoyo\n\nIt's necessary to bootstrap Yoyo with a few configuration settings. This code should run when rendering and updating components.\n\n```php\nuse Clickfwd\\Yoyo\\View;\nuse Clickfwd\\Yoyo\\ViewProviders\\YoyoViewProvider;\nuse Clickfwd\\Yoyo\\Yoyo;\n\n$yoyo = new Yoyo();\n\n$yoyo-\u003econfigure([\n  'url' =\u003e '/yoyo',\n  'scriptsPath' =\u003e 'app/resources/assets/js/',\n  'namespace' =\u003e 'App\\\\Yoyo\\\\'\n]);\n\n// Register the native Yoyo view provider \n// Pass the Yoyo components' template directory path in the constructor\n\n$yoyo-\u003eregisterViewProvider(function() {\n  return new YoyoViewProvider(new View(__DIR__.'/resources/views/yoyo'));\n});\n```\n\n**'url'**\n\nAbsolute or relative URL that will be used to request component updates.\n\n**'scriptsPath'**\n\nThe location where you copied the `yoyo.js` script. \n\n**'namespace'**\n\nThis is the PHP class namespace that will be used to discover auto-loaded dynamic components (components that use a PHP class). \n\nIf the namespace is not provided or components are in different namespaces, you need to register them manually:\n\n```php\n$yoyo-\u003eregisterComponents([\n    'counter' =\u003e App\\Yoyo\\Counter::class,\n];\n```\n\nYou are required to load the component classes at run time, either using a `require` statement to load the component's PHP class file, or by including your component namespaces in you project's `composer.json`.\n\nAnonymous components don't need to be registered, but the template name needs to match the component name.\n\n### Load Assets\n\nFind `yoyo.js` in the following vendor path and copy it to your project's public assets directory.\n\n```file\n/vendor/clickfwd/yoyo/src/assets/js/yoyo.js \n```\n\nTo load the necessary scripts in your template add the following code inside the `\u003chead\u003e` tag:\n\n```php\n\u003c?php yoyo_scripts(); ?\u003e\n```\n\n## Creating Components\n\nDynamic components require a class and a template. When using the Blade and Twig view providers, you can also use inline views, where the component markup is returned directly in the component's `render` method.\n\nAnonymous components allow creating components with just a template file.\n\nTo create a simple search component that retrieves results from the server and updates itself, create the component template:\n\n```html\n// resources/views/yoyo/search.php\n\n\u003cform\u003e\n    \u003cinput type=\"text\" name=\"query\" value=\"\u003c?php echo $query ?? ''; ?\u003e\"\u003e\n    \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e\n```\n\nYoyo will render the component output and compile it to add the necessary attributes that makes it dynamic and reactive. \n\nWhen you submit the form, posted data is automatically made available within the component template. The template code can be expanded to show a list of results, or an empty state:\n\n```php\n\u003c?php\n$query = $query ?? '';\n$entries = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];\n$results = array_filter($entries, function($entry) use ($query) {\n    return $query \u0026\u0026 strpos($entry, $query) !== false;\n});\n?\u003e\n```\n\n```html\n\u003cform\u003e\n    \u003cinput type=\"text\" name=\"query\" value=\"\u003c?php echo $query; ?\u003e\"\u003e\n    \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n\u003c/form\u003e\n    \n\u003cul\u003e\n    \u003c?php if ($query \u0026\u0026 empty($results)): ?\u003e\n        \u003cli\u003eNo results found\u003c/li\u003e\n    \u003c?php endif; ?\u003e\n    \n    \u003c?php foreach ($results as $entry): ?\u003e\n        \u003cli\u003e\u003c?php echo $entry; ?\u003e\u003c/li\u003e\n    \u003c?php endforeach; ?\u003e\n\u003c/ul\u003e\n```\n\nThe `$results` array can be populated from any source (i.e. database, API, etc.)\n\nThe example can be converted into a live search input, with a 300ms debounce to minimize the number of requests. Replace the `form` tag with:\n\n```html\n\u003cinput yoyo:on=\"keyup delay:300ms changed\" type=\"text\" name=\"query\" value=\"\u003c?php echo $query; ?\u003e\" /\u003e\n```\n\nThe `yoyo:on=\"keyup delay:300ms change\"` directive tells Yoyo to make a request on the keyup event, with a 300ms debounce, and only if the input text changed. \n\nNow let's turn this into a dynamic component using a class.\n\n```php\n# /app/Yoyo/Search\n\n\u003c?php\n\nnamespace App\\Yoyo;\n\nuse Clickfwd\\Yoyo\\Component;\n\nclass Search extends Component\n{\n\tpublic $query;\n\t\n\tprotected $queryString = ['query'];\n\t\n\tpublic function render()\n\t{\n\t\t$query = $this-\u003equery;\n\t\n\t\t// Perform your database query\n\t\t$entries = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];\n\t\n\t\t$results = array_filter($entries, function($entry) use ($query) {\n\t\t\treturn $query \u0026\u0026 stripos($entry, $query) !== false;\n\t\t});\n\t\n\t  // Render the component view\n\t\treturn $this-\u003eview('search',['results' =\u003e $results]);\n\t}\n}\n```\n\nAnd the template:\n\n```html\n\u003c!-- /app/resources/views/yoyo/search.php --\u003e\n\n\u003cinput yoyo:on=\"keyup delay:300ms changed\" type=\"text\" name=\"query\" value=\"\u003c?php echo $query; ?\u003e\" /\u003e\n\n\u003cul yoyo:ignore\u003e\n    \u003c?php if ($query \u0026\u0026 empty($results)): ?\u003e\n        \u003cli\u003eNo results found\u003c/li\u003e\n    \u003c?php endif; ?\u003e\n    \n    \u003c?php foreach ($results as $entry): ?\u003e\n        \u003cli\u003e\u003c?php echo $entry; ?\u003e\u003c/li\u003e\n    \u003c?php endforeach; ?\u003e\n\u003c/ul\u003e\n```\n\nA couple of things to note here that are covered in more detail in other sections.\n\n1. The component class includes a `queryString` property that tells Yoyo to automatically include the queryString values in the browser URL after a component update. If you re-load the page with the `query` value in the URL, you'll automatically see the search results on the page.\n2. Yoyo will automatically make available component class public properties as template variables. This allows using `$this-\u003equery` to access the search keyword in the component and `$query` in the template.\n\nWhen you compare this search example to the counter example at the beginning, you can see that there are no action methods (i.e. increment, decrement). A component update will always default to the `render` method, unless an action is specified via one of the method attributes (i.e. yoyo:get, yoyo:post, etc.). In that case, the action method always runs before the render method.\n\n## Rendering Components\n\nThere are two instances when components are rendered. On page load, and on component updates.\n\n### Rendering on Page Load\n\nTo render any component on page load within your templates, use the `yoyo_render` function and pass the component name as the first parameter.\n\n```php\n\u003c?php echo yoyo_render('search'); ?\u003e\n```\n\nFor dynamic components, the component name is a hyphenated version of the class name (i.e. LiveSearch → live-search). If you register components while bootstrapping Yoyo using the `registerComponents` method, then you can use the registered alias as the component name.\n\n```php\n$yoyo-\u003eregisterComponent('search', App\\Yoyo\\LiveSearch::class);\n```\n\nFor anonymous components, the component name should match the template name without the file extension. So if the template name is `form.php`, the component can be rendered with:\n\n```php\n\u003c?php echo yoyo_render('form'); ?\u003e\n```\n\n### Rendering on updates\n\nUse the `yoyo_update` function to automatically process the component request and output the updated component.\n\n```php\n\u003c?php echo yoyo_update(); ?\u003e\n```\n\nYou need to add this function call for requests routed to the Yoyo `url` used in the initial configuration.\n\n## Properties\n\nIn dynamic components, all public properties in the component class are automatically made available to the view and tracked in component updates.\n\n```php\nclass HelloWorld extends Component\n{\n    public $message = 'Hello World!';\n}\n```\n\n```html\n\u003cdiv\u003e\n    \u003ch1\u003e\u003c?php echo $message; ?\u003e\u003c/h1\u003e\n    \u003c!-- Will output \"Hello World!\" --\u003e\n\u003c/div\u003e\n```\n\nPublic properties should only be of type: `string`, `int`, `array`, `boolean`, and should not contain any sensitive information because they can be used in component requests to keep the data in sync.\n\n### Initializing Properties\n\nYou can initialize properties using the `mount` method of your component which runs right after the component is instantiated, and before the `render` method.\n\n```php\nclass HelloWorld extends Component\n{\n    public $message;\n\n    public function mount()\n    {\n        $this-\u003emessage = 'Hello World!';\n    }\n}\n```\n\n### Data Binding\n\nYou can automatically bind, or synchronize, the value of an HTML element with a component public property.\n\n```php\nclass HelloWorld extends Component\n{\n    public $message = 'Hello World!';\n}\n```\n\n```html\n\u003cdiv\u003e\n    \u003cinput yoyo name=\"message\" type=\"text\" value=\"\u003c?php echo $message; ?\u003e\"\u003e\n    \u003ch1\u003e\u003c?php echo $message;?\u003e\u003c/h1\u003e\n\u003c/div\u003e\n``` \n\nAdding the `yoyo` attribute to any input will instantly make it reactive. Any changes to the input will be updated in the component.\n\nBy the default, the natural event of an element will be used as the event trigger. \n\n- input, textarea and select elements are triggered on the change event.\n- form elements are triggered on the submit event.\n- All other elements are triggered on the click event.\n\nYou can modify this behavior using the `yoyo:on` directive which accepts multiple events separated by comma:\n\n ```html\n \u003cinput yoyo:on=\"keyup\" name=\"message\" type=\"text\" value=\"\u003c?php echo $message; ?\u003e\"\u003e\n ```\n\n### Debouncing and Throttling Requests\n\nThe are several ways to limit the requests to update components.\n\n**`delay`** - debounces the request so it's made only after the specified period passes after the last trigger.\n\n```html\n\u003cinput yoyo:on=\"keyup delay:300ms\" name=\"message\" type=\"text\" value=\"\u003c?php echo $message; ?\u003e\"\u003e\n```\n\n**`throttle`** limits request to one dwithin the specified interval.\n\n```html\n\u003cinput yoyo:on=\"input throttle:2s\" name=\"message\" type=\"text\" value=\"\u003c?php echo $message; ?\u003e\"\u003e\n```\n\n**`changed`** - only makes the request when the input value has changed.\n\n```html\n\u003cinput yoyo:on=\"keyup delay:300ms changed\" name=\"message\" type=\"text\" value=\"\u003c?php echo $message; ?\u003e\"\u003e\n```\n\n## Actions\n\nAn action is a request made to a Yoyo component method to update (re-render) it as a result of a user interaction or page event (click, mouseover, scroll, load, etc.).\n\nThe `render` method is the default action when one is not provided explicitly. You can also override it in the component class to change the template name or when you need to send additional variables to the template in addition to the public properties.\n\n```php\npublic function render() \n{\n\treturn $this-\u003eview($this-\u003ecomponentName, ['foo' =\u003e 'bar']);\n}\n```\n\nTo specify an action you use one of the available action directives with the name of the action as the value.\n\n- `yoyo:get`\n- `yoyo:post`\n- `yoyo:put`\n- `yoyo:patch`\n- `yoyo:delete`\n\nFor example:\n\n```php\nclass Review extends Component\n{\n    public Review $review;\n\n    public function helpful()\n    {\n        $this-\u003ereview-\u003euserFoundHelpful($userId);\n    }\n}\n```\n\n```html\n\u003cdiv\u003e\n    \u003cbutton yoyo:on=\"click\" yoyo:get=\"helpful\"\u003eFound Helpful\u003c/button\u003e\n\u003c/div\u003e\n```\n\nAll components automatically listen for the `refresh` event and trigger the `render` action to refresh the component state.\n\n### Passing Data to Actions\n\nYou can include additional data to send to the server on component update requests using the `yoyo:vals` directive which accepts a JSON encoded list of name-value pairs.\n\n```html\n\u003cbutton yoyo:on=\"click\" yoyo:get=\"helpful\" yoyo:vals='{\"reviewId\":100}'\u003eFound Helpful\u003c/button\u003e\n\n\u003c!-- Or use the encode_vals helper function to pass an array of name-value pairs --\u003e\n\u003cbutton yoyo:on=\"click\" yoyo:get=\"helpful\" yoyo:vals='\u003c?php Yoyo\\encode_vals([\"reviewId\"=\u003e 100]); ?\u003e'\u003eFound Helpful\u003c/button\u003e\n```\n\nYou can also use `yoyo:val.name` for individual values. kebab-case variable names are automatically converted to camel-case.\n\n```html\n\u003cbutton yoyo:on=\"click\" yoyo:get=\"helpful\" yoyo:val.review-id=\"100\"\u003eFound Helpful\u003c/button\u003e\n```\n\nYoyo will automatically track and send component public properties and input values with every request. \n\n```php\nclass Review extends Component {\n\n\tpublic $reviewId;\n\n\tpublic function helpful()\n\t{\n\t\t// access reviewId via $this-\u003ereviewId\n\t}\n}\n```\n\nYou can also pass extra parameters to an action as arguments using an expression, without having to define them as public properties in the component:\n\n```html\n\u003cbutton yoyo:get=\"addToCart(\u003c?php echo $productId; ?\u003e, '\u003c?php echo $style; ?\u003e')\"\u003e\n    Add Todo\n\u003c/button\u003e\n```\n\nExtra parameters passed to an action are made available to the component method as regular arguments:\n\n```php\npublic function addToCart($productId, $style)\n{\n    // ...\n}\n```\n\n### Actions Without a Response\n\nSometimes you may want to use a component action only to make changes to a database and trigger events, without rendering a response. You can use the component `skipRender` method for this:\n\n```php\npublic function savePost() \n{\n\t// Store the post to the database\n\n\t// Send event to the browser to close modal, or trigger a notification\n\t$this-\u003eemitSelf('PostSaved');\n\n\t// Skip template rendering\n\t$this-\u003eskipRender();\n}\n```\n\n## View Data\n\nSometimes you want to send data to a view without declaring the variable as a public property. You can do this by defining a render method in your component and passing a data array as the second argument:\n\n```php\npublic function render() \n{\n\treturn $this-\u003eview($this-\u003ecomponentName, ['foo' =\u003e 'bar']);\n}\n```\n\nThen access the $foo variable in your template.\n\nYou can also send data to the component view using the `set` method in any component action. For example:\n\n```php\npublic function increment()\n{\n\t$this-\u003eset('foo', 'bar');\n\t// or\n\t$this-\u003eset(['foo' =\u003e 'bar']);\n}\n```\n\n## Computed Properties\n\n```php\nclass HelloWorld extends Component\n{\n\tpublic $message = 'Hello World!';\n\t\n   \t// Computed Property\n\tpublic function getHelloWorldProperty()\n\t{\n\t\treturn $message;\n\t}\n\n   \t// Computed Property with argument\n\tpublic function getErrorsProperty($name)\n\t{\n\t\treturn [\n\t\t\t'title' =\u003e 'Please enter a title',\n\t\t\t'description' =\u003e 'Please enter a description',\n\t\t][$name] ?? null;\n\t}\n}\n```\n\t\nNow, you can access `$this-\u003ehello_world` from either the component's class or template:\n\n```php\n\u003cdiv\u003e\n\t\u003ch1\u003e\u003c?php echo $this-\u003ehello_world ;?\u003e\u003c/h1\u003e\n\t\u003c!-- Will output \"Hello World!\" --\u003e\n\u003c/div\u003e\n```\n\nComputed properties with arguments behave like normal class methods that you can call in your templates:\n\n```php\n\u003cdiv\u003e\n\t\u003ch1\u003e\u003c?php echo $this-\u003eerrors('title') ;?\u003e\u003c/h1\u003e\n\t\u003c!-- Will output \"Please enter a title\" --\u003e\n\u003c/div\u003e\n```\n\nThe output of computed properties is cached within the same component request, allowing you to perform complex tasks like querying the database and not duplicating the tasks if the property is accessed multiple times. If you need to clear the cache for a computed property:\n\n```php\n// Clear all computed properties, including those with arguments\n$this-\u003eforgetComputed();\n\n// Clear a single property\n$this-\u003eforgetComputed($property);\n\n// Clear multiple properties\n$this-\u003eforgetComputed([$property1, $property2]);\n\n// Clear a single computed property with arguments\n$this-\u003eforgetComputedWithArgs($property, $arg1, $arg2);\n```\n\n## Component Props\n\nYoyo can persist and update variables in requests without the need to explicitly include an input element.\n\nFor an anonymous component, it's possible to specify the props directly in the component root node using a comma-separated list of variable names and this allows implementing a counter without the need for a component class:\n\n```php\n\u003c?php $count = $count ?? 0 ; ?\u003e\n\u003cdiv yoyo:props=\"count\"\u003e\n\t\u003cbutton yoyo:val.count=\"\u003c?php echo $count + 1; ?\u003e\"\u003e+\u003c/button\u003e \n    \u003cp\u003e\u003c?php echo $count; ?\u003e\u003c/p\u003e\n\u003c/div\u003e\n```\n\nBy adding the `yoyo:props=\"count\"`, Yoyo knows to automatically include the value of `count` in every request.\n\nFor dynamic components, there's no need to use the `yoyo:props` attribute because we use the protected method $props in the component class with an array of variable names.\n\n```php\nclass Counter extends Component\n{\n\tpublic $count = 0;\n\t\n\tprotected $props = ['count'];\n\n    public function increment()\n    {\n        $this-\u003ecount++;\n    }\n}\n```\n\nSince the `$count` variable is also defined as a public property, it's already available in the template and the value is incremented throgh the `increment` method in the component class without having to use `yoyo:val.count`.\n\n```html\n\u003cdiv\u003e\n\t\u003cbutton yoyo:get=\"increment\"\u003e+\u003c/button\u003e\n\t\u003cspan\u003e\u003c?php echo $count; ?\u003e\u003c/span\u003e\n\u003c/div\u003e\n```\n\n## Query String\n\nComponents have the ability to automatically update the browser's query string on state changes. \n\n```php\nclass Search extends Component\n{\n\tpublic $query;\n\t\n\tprotected $queryString = ['query'];\n}\n```\n\nYoyo is smart enough to automatically remove the query string when the current state value matches the property's default value.\n\nFor example, in a pagination component, you don't need the `?page=1` query string to appear in the URL.\n\n```php\nclass Posts extends Component\n{\n\tpublic $page = 1;\n\t\n\tprotected $queryString = ['page'];\n}\n```\n\n## Loading States\n\nUpdating Yoyo components requires an Ajax request to the server and depending on what the component does, the response time will vary. The `yoyo:spinning` directive allows you to do all sorts of cool things when a component is updating to provide a visual indicator to end-users.\n\n### Toggling Elements During Loading States\n\nTo show an element at the start of a Yoyo update request and hide it again when the update is complete:\n\n```html\n\u003cdiv\u003e\n    \u003cbutton yoyo:post=\"submit\"\u003eSubmit\u003c/button\u003e\n\n    \u003cdiv yoyo:spinning\u003e\n        Processing your submission...\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\nYoyo adds some CSS to the page to automatically hide the element with the `yoyo:spinning` directive.\n\nTo hide a visible element while the component is updating you can add the `remove` modifier:\n\n```html\n\u003cdiv\u003e\n    \u003cbutton yoyo:post=\"submit\"\u003eSubmit\u003c/button\u003e\n\n    \u003cdiv yoyo:spinning.remove\u003e\n        Text hidden while updating ...\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\n## Delaying Loading States\n\nSome actions may update quickly and showing a loading state in these cases may be more of a distraction. The `delay` modifier ensures that the loading state changes are applied only after 200ms if the component hasn't finished updating.\n\n```html\n\u003cdiv\u003e\n    \u003cbutton yoyo:post=\"submit\"\u003eSubmit\u003c/button\u003e\n\n    \u003cdiv yoyo:spinning.delay\u003e\n        Processing your submission...\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\n### Targeting Specific Actions\n\nIf you need to toggle different indicators for different component actions, you can add the `yoyo:spin-on` directive and pass a comma separated list of action names. For example:\n\n```html\n\u003cdiv\u003e\n\t\u003cbutton yoyo:get=\"edit\"\u003eEdit\u003c/button\u003e\n\n\t\u003cbutton yoyo:get=\"like\"\u003eLike\u003c/button\u003e\n\n    \u003cdiv yoyo:spinning yoyo:spin-on=\"edit\"\u003e\n        Show for edit action\n    \u003c/div\u003e\n\n    \u003cdiv yoyo:spinning yoyo:spin-on=\"like\"\u003e\n        Show for like action\n    \u003c/div\u003e\n\n    \u003cdiv yoyo:spinning yoyo:spin-on=\"edit, like\"\u003e\n        Show for edit and like actions\n    \u003c/div\u003e\n\n\u003c/div\u003e\n```\n\n## Toggling Element CSS Classes\n\nInstead of toggling the visibility of an element you can also add specific CSS classes while the component updates. Use the `class` modifier and include the space-separated class names as the attribute value:\n\n```html\n\u003cdiv\u003e\n    \u003cbutton yoyo:post=\"submit\" yoyo:spinning.class=\"text-gray-300\"\u003e\n\t\tSubmit\n\t\u003c/button\u003e\n\u003c/div\u003e\n```\n\nYou can also remove specific class names by adding the `remove` modifier:\n\n```html\n\u003cdiv\u003e\n    \u003cbutton yoyo:post=\"submit\" yoyo:spinning.class.remove=\"bg-blue-200\" class=\"bg-blue-200\"\u003e\n\t\tSubmit\n\t\u003c/button\u003e\n\u003c/div\u003e\n```\n\n## Toggling Element Attributes\n\nSimilar to CSS class toggling, you can also add or remove attributes while the component is updating.\n\n```html\n\u003cdiv\u003e\n    \u003cbutton yoyo:post=\"submit\" yoyo:spinning.attr=\"disabled\"\u003e\n\t\tSubmit\n\t\u003c/button\u003e\n\u003c/div\u003e\n```\n\n## Events\n\nEvents are a great way to establish communication between Yoyo components on the same page, where one or more components can listen to events fired by another component.\n\nEvents can be fired from component methods and templates using a variety of emit methods.\n\nAll emit methods accept any number of arguments that allow sending data (string, number, array) to listeners.\n\n### Emitting an Event to All Yoyo Components\n\nFrom a component method.\n\n```php\npublic function increment()\n{\n\t$this-\u003ecount++;\n\t\t\n\t$this-\u003eemit('counter-updated', $count);\n}\n```\n\nFrom a template\n\n```php\n\u003c?php $this-\u003eemit('counter-updated', $count) ; ?\u003e\n```\n\n### Emitting an Event to Parent Components\n\nWhen dealing with nested components you can emit events to parents and not children or sibling components.\n\n```php\n$this-\u003eemitUp('postWascreated', $arg1, $arg2);\n```\n\n### Emitting an Event to a Specific Component\n\nWhen you need to emit an event to a specific component using the component name (e.g. `cart`).\n\n```php\n$this-\u003eemitTo('cart', 'productAddedToCart', $arg1, $arg2);\n```\n\n### Emitting an Event to an Element Using a Selector\n\nThe `emitTo` method also works with selectors. When a component is not found, the selector is used instead. Emitting events using selectors doesn't support passing arguments. \n\n```php\n$this-\u003eemitTo('.cart', 'productAddedToCart');\n$this-\u003eemitTo('#cart', 'productAddedToCart');\n$this-\u003eemitTo('.post-100', 'saved');\n```\n\n#### Emitting an Event to Itself\n\nWhen you need to emit an event on the same component.\n\n```php\n$this-\u003eemitSelf('productAddedToCart', $arg1, $arg2);\n```\n\n### Listening for Events\n\nTo register listeners in Yoyo, use the `$listeners` protected property of the component.\n\nListeners are a key-\u003evalue pair where the key is the event to listen for, and the value is the method to call on the component. If the event and method are the same, you can leave out the key.\n\n```php\nclass Counter extends Component {\n\n\tpublic $message;\n\n\tprotected $listeners = ['counter-updated' =\u003e 'showNewCount'];\n\n\tprotected function showNewCount($count)\n\t{\n\t\t$this-\u003emessage = \"The new count is: $count\";\n\t}\n}\n```\n\n### Listening For Events In JavaScript\n\nYoyo allows registering event listeners for component emitted events:\n\n```js\n\u003cscript\u003e\nYoyo.on('productAddedToCart', id =\u003e {\n\talert('A product was added to the cart with ID:' + id\n});\n\u003c/script\u003e\n```\n\nWith this feature you can control toasters, alerts, modals, etc. directly from a component action on the server by emitting the event and listening for it on the browser.\n\n### Dispatching Browser Events\n\nIn addition to allowing components to communicate with each other, you can also send browser window events directly from a component method or template:\n\n```php\n// passing single value\n$this-\u003edispatchBrowserEvent('counter-updated', $count);\n\n// Passing an array\n$this-\u003edispatchBrowserEvent('counter-updated', ['count' =\u003e $count]);\n```\n\nAnd listen for the event anywhere on the page:\n\n```js\n\u003cscript\u003e\nwindow.addEventListener('counter-updated', event =\u003e {\n\t// Reading a single value\n\talert('Counter is now: ' + event.detail);\n\n\t// Reading from an array\n\talert('Counter is now: ' + event.detail.count);\n})\n\u003c/script\u003e\n```\n## Redirecting\n\nSometimes you may want to redirect the user to a different page after performing an action within a Yoyo component.\n\n```php\nclass Registration extends Component\n{\n    public function register()\n    {\n\t// Create the user \n\n\t$this-\u003eredirect('/welcome');\n    }\n}\n```\n\n## Using Blade\n\nYou can use Yoyo with Laravel's [Blade](https://laravel.com/docs/8.x/blade) templating engine, without having to use Laravel.\n\n### Installation\n\nTo get started install the following packages in your project:\n\n```bash\ncomposer require clickfwd/yoyo\ncomposer require jenssegers/blade\n```\n\n### Configuration\n\nCreate a Blade instance and set it as the view provider for Yoyo. We also add the `YoyoServiceProvider` for Blade.\n\nThis code should run when rendering and updating components.\n\n```php\n\u003c?php\n\nuse Clickfwd\\Yoyo\\Blade\\Application;\nuse Clickfwd\\Yoyo\\Blade\\YoyoServiceProvider;\nuse Clickfwd\\Yoyo\\ViewProviders\\BladeViewProvider;\nuse Clickfwd\\Yoyo\\Yoyo;\nuse Illuminate\\Contracts\\Foundation\\Application as ApplicationContract;\nuse Illuminate\\Contracts\\View\\Factory as ViewFactory;\nuse Illuminate\\Support\\Fluent;\nuse Jenssegers\\Blade\\Blade;\n\ndefine('APP_PATH', __DIR__);\n\n$yoyo = new Yoyo();\n\n$yoyo-\u003econfigure([\n  'url' =\u003e 'yoyo',\n  'scriptsPath' =\u003e APP_PATH.'/app/resources/assets/js/',\n  'namespace' =\u003e 'App\\\\Yoyo\\\\',\n]);\n\n// Create a Blade instance\n\n$app = Application::getInstance();\n\n$app-\u003ebind(ApplicationContract::class, Application::class);\n\n// Needed for Blade anonymous components\n\n$app-\u003ealias('view', ViewFactory::class);\n\n$app-\u003eextend('config', function (array $config) {\n    return new Fluent($config);\n});\n\n$blade = new Blade(\n    [\n        APP_PATH.'/resources/views',\n        APP_PATH.'/resources/views/yoyo',\n        APP_PATH.'/resources/views/components',\n    ],\n    APP_PATH.'/../cache',\n    $app\n);\n\n$app-\u003ebind('view', function () use ($blade) {\n    return $blade;\n});\n\n(new YoyoServiceProvider($app))-\u003eboot();\n\n// Optionally register Blade components\n\n$blade-\u003ecompiler()-\u003ecomponents([\n    // 'button' =\u003e 'button',\n]);\n\n// Register Blade view provider for Yoyo\n\n$yoyo-\u003eregisterViewProvider(function() use ($blade) {\n    return new BladeViewProvider($blade);\n});\n```\n\n### Load Assets\n\nFind `yoyo.js` in the following vendor path and copy it to your project's public assets directory.\n\n```file\n/vendor/clickfwd/yoyo/src/assets/js/yoyo.js \n```\n\nTo load the necessary scripts in your Blade template you can use the `yoyo_scripts` directive in the `\u003chead\u003e` tag:\n\n```blade\n@yoyo_scripts\n```\n\n### Rendering a Blade View\n\nYou can use the Blade instance to render any Blade view.\n\n```\n$blade = \\Clickfwd\\Yoyo\\Yoyo::getViewProvider()-\u003egetProviderInstance();\n\necho $blade-\u003erender('home');\n```\n\n### Rendering Yoyo Blade Components\n\nTo render Yoyo components inside Blade views, use the `@yoyo` directive.\n\n```blade\n@yoyo('search')\n```\n\n### Updating Yoyo Blade Components\n\nTo update Yoyo components in the Yoyo-designated route.\n\n```php\necho (new \\Clickfwd\\Yoyo\\Blade\\Yoyo())-\u003eupdate();\n```\n\n### Inline Views\n\nWhen dealing with simple templates, you can create components without a template file and instead return an inline view in the component's `render` method.\n\n```php\nclass HelloWorld extends Component\n{\n    public $message = 'Hello World!';\n}\n\npublic function render()\n{\n\treturn \u003c\u003c\u003c'yoyo'\n\t\t\u003cdiv\u003e\n\t\t    \u003cinput yoyo name=\"message\" type=\"text\" value=\"{{ $message }}\"\u003e\n\t\t    \u003ch1\u003e{{ $message }}\u003c/h1\u003e\n\t\t\u003c/div\u003e\t\t\n\tyoyo;\n}\n```\n\n### Other Blade Features\n\nYoyo implements several Blade directives that can be used within Yoyo component templates.\n\n- `@spinning` and `@endspinning` - Check if a component is being re-rendered. \n\n\t```blade\n\t@spinnning\n\tComponent updated\n\t@endspinning\n\t\n\t@spinning($liked == 1)\n\tComponent updated and liked == 1\n\t@endspinning\n\t```\n\n- All event methods are available as directives within blade components\n\n\t```blade\n\t@emit('eventName', ['foo' =\u003e 'bar']);\n\t@emitUp('eventName', ['foo' =\u003e 'bar']);\n\t@emitSelf('eventName', ['foo' =\u003e 'bar']);\n\t@emitTo('component-name', 'eventName', ['foo' =\u003e 'bar']);\n\t```\n    \n- Computed properties\n\n\t```php\n\tclass HelloWorld extends Component\n\t{\n\t    public $message = 'Hello World!';\n\n\t    public function getHelloWorldProperty()\n\t    {\n\t\t    return $message;\n\t    }\n\t}\n\t```\n\n\t```blade\n\t\u003cdiv\u003e\n\t    \u003ch1\u003e{{ $this-\u003ehello_world }}\u003c/h1\u003e\n\t    \u003c!-- Will output \"Hello World!\" --\u003e\n\t\u003c/div\u003e\n\t```\n\n## Using Twig\n\nYou can use Yoyo with Symfony's [Twig](https://twig.symfony.com/) templating engine.\n\n### Installation\n\nTo get started install the following packages in your project:\n\n```bash\ncomposer require clickfwd/yoyo\ncomposer require twig/twig\n```\n\n### Configuration \n\nCreate a Twig instance and set it as the view provider for Yoyo. We also add the `YoyoTwigExtension` to Twig.\n\nThis code should run when rendering and updating components.\n\n```php\nuse Clickfwd\\Yoyo\\Twig\\YoyoTwigExtension;\nuse Clickfwd\\Yoyo\\ViewProviders\\TwigViewProvider;\nuse Clickfwd\\Yoyo\\Yoyo;\nuse Twig\\Extension\\DebugExtension;\n\ndefine('APP_PATH', __DIR__);\n\n$yoyo = new Yoyo();\n\n$yoyo-\u003econfigure([\n  'url' =\u003e 'yoyo',\n  'scriptsPath' =\u003e APP_PATH.'/app/resources/assets/js/',\n  'namespace' =\u003e 'App\\\\Yoyo\\\\',\n]);\n\n$loader = new \\Twig\\Loader\\FilesystemLoader([\n  APP_PATH.'/resources/views',\n  APP_PATH.'/resources/views/yoyo',\n]);\n\n$twig = new \\Twig\\Environment($loader, [\n  'cache' =\u003e APP_PATH.'/../cache',\n  'auto_reload' =\u003e true,\n  // 'debug' =\u003e true\n]);\n\n// Add Yoyo's Twig Extension\n\n$twig-\u003eaddExtension(new YoyoTwigExtension());\n\n// Register Twig view provider for Yoyo\n\n$yoyo-\u003eregisterViewProvider(function() use ($twig) {\n  return new TwigViewProvider($twig);\n});\n```\n\n### Load Assets\n\nFind `yoyo.js` in the following vendor path and copy it to your project's public assets directory.\n\n```file\n/vendor/clickfwd/yoyo/src/assets/js/yoyo.js \n```\n\nTo load the necessary scripts in your Twig template you can use the `yoyo_scripts` function in the `\u003chead\u003e` tag:\n\n```twig\n{{ yoyo_scripts() }}\n```\n\n### Rendering a Twig View\n\nYou can use the Twig instance to render any Twig view.\n\n```\n$twig = \\Clickfwd\\Yoyo\\Yoyo::getViewProvider()-\u003egetProviderInstance();\n\necho $twig-\u003erender('home');\n```\n\n### Rendering Yoyo Twig Components\n\nTo render Yoyo components inside Twig views, use the `yoyo` function.\n\n```twig\nyoyo('search')\n```\n\n### Updating Yoyo Twig Components\n\nTo update Yoyo components in the Yoyo-designated route.\n\n```php\necho (new \\Clickfwd\\Yoyo\\Yoyo())-\u003eupdate();\n```\n\n### Inline Views\n\nWhen dealing with simple templates, you can create components without a template file and instead return an inline view in the component's `render` method.\n\n```php\nclass HelloWorld extends Component\n{\n    public $message = 'Hello World!';\n}\n\npublic function render()\n{\n\treturn \u003c\u003c\u003c'twig'\n\t\t\u003cdiv\u003e\n\t\t    \u003cinput yoyo name=\"message\" type=\"text\" value=\"{{ message }}\"\u003e\n\t\t    \u003ch1\u003e{{ message }}\u003c/h1\u003e\n\t\t\u003c/div\u003e\t\t\n\ttwig;\n}\n```\n\n### Other Twig Features\n\nYoyo adds a few functions and variables that can be used within Yoyo component templates.\n\n- The `spinning` variable can be used to check if a component is being re-rendered. \n\n\t```twig\n\t{% if spinning %}\n\tComponent updated\n\t{% endif %}\n\t```\t\n\n- All event methods are available as functions within blade components\n\t\n\t```twig\n\t{{ emit('eventName', {'foo':'bar'}) }}\n\t{{ emitUp('eventName', {'foo':'bar'}) }}\n\t{{ emitSelf('eventName', {'foo':'bar'}) }}\n\t{{ emitTo('component-name', 'eventName', {'foo':'bar'}) }}\n\t```\n\t\n- Computed properties\n\n\t```php\n\tclass HelloWorld extends Component\n\t{\n\t    public $message = 'Hello World!';\n\n\t    public function getHelloWorldProperty()\n\t    {\n\t\t    return $this-\u003emessage;\n\t    }\n\t}\n\t```\n\t\n\t```twig\n\t\u003cdiv\u003e\n\t\t\u003ch1\u003e{{ this.hello_world }}\u003c/h1\u003e\n\t\t\u003c!-- Will output \"Hello World!\" --\u003e\n\t\u003c/div\u003e\n\t```\n\n\n## License\n\nCopyright © ClickFWD\n\nYoyo is open-sourced software licensed under the [MIT license](LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclickfwd%2Fyoyo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclickfwd%2Fyoyo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclickfwd%2Fyoyo/lists"}