Creating Layouts & Components with AlpineJS - alpine.js

I am wondering -- is there a way to create layouts and/or components with AlpineJS (so that I can keep my code DRY)? If not, are there any solutions that integrate with AlpineJS that add this functionality (without having to resort to a full framework like React or Vue)?

Alpine.js tries to avoid templating as much as possible since it's designed to work in tandem with server-side templating or a static side generator.
The example per the Alpine.js docs to load HTML partials is to use x-init="fetch('/path/to/my/file.html).then(r => r.text()).then(html => $refs.someRef.innerHTML = html)" (x-init is just one spot where this could be done).

You could use x-component with named slot in vimesh ui
<head>
<script src="https://unpkg.com/#vimesh/ui"></script>
<script src="https://unpkg.com/alpinejs" defer></script>
</head>
<body>
<vui-card>
<template slot="title">TITLE</template>
This is content
<template slot="footer">Copyright</template>
</vui-card>
<template x-component="card">
<h1>
<slot name="title"></slot>
</h1>
<p>
<slot></slot>
</p>
<div>
<slot name="footer"></slot>
</div>
</template>
</body>

If you only need small components you can use x-html and x-data:
<div x-data="{ message: '<p>Hello <strong>World!</strong></p>' }">
<span x-html="message"></span>
<span x-html="message"></span>
</div>
This would return:
Hello World!
Hello World!
You can read the docs here

Related

2 way binding with Vue3 Inline template (using slots) in laravel blade template files

In Vue3, inline-templates were depreciated and now slots are used. Is it possible to have 2-way binding of variables for Vuejs components written in blade templates?
I want to have 2-way binding for Vue components that's written inline with blade templates. Although I know I can pass data like <example-component name="Hello World"> It is a ton of work to add props everywhere.
Vue recommends using slots as a inline-template replacement since it got removed in v3, however, that documentation makes no sense. I've got the components displayed using the code below. It's a dead simple text field + paragraph to display the name.
home.blade.php (Removed unnecessary HTML for brevity)
<div>
<h1>Dashboard</h1>
<example-component>
<div class="container">
<input v-model="name" placeholder="Change Name"/>
<p> Name is #{{ name }} </p>
</div>
</example-component>
</div>
example-component.vue
<template>
<div>
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return {
name: 'hi',
}
}
}
</script>
Unfortunately, this does not work, the name doesn't start as 'hi' and doesn't update when changing the textfield. When I try something like <slot :name=name></slot>. Which I believe would pass the name into the slots section, the component gets rendered for a second before disappearing.
Is having 2-way binding with vue variables in blade templates even possible? Any help is appreciated.
Vue: 3.0.5
Laravel: 8.29.0
Is there a reason you're storing the data in the child component? The reactivity design works by passing props down and emitting events up, even though (unfortunately) the reactivity is not maintained when passing a variable up to the parent component. Seems a little counter intuitive, but I might be missing something in what you're trying to create.
It will, however, work if you put the data into the app instead of the component.
// app
const app = Vue.createApp({
data() {
return {
name: 'hi',
}
}
})
// component
app.component('example-component', {
template: `
<div>
<slot></slot>
</div>`,
})
app.mount("#app");
<script src="https://unpkg.com/vue#3.0.5/dist/vue.global.prod.js"></script>
<div id="app">
<h1>Dashboard</h1>
<example-component>
<div class="container">
<input v-model="name" placeholder="Change Name"/>
<p> Name is #{{ name }} </p>
</div>
</example-component>
</div>
<!--

Nesting custom tags in Vue

I have a Laravel / Vue app that I'm building and I've run into a bit of a snag. I have been able to successfully create individual stand-alone components, however when I try to nest a stand-alone component into another component, only the nested component shows up. A bit of code:
CompanyLogo.vue
<template>
<figure class="company-logo" :style="{
width: size,
height: size,
backgroundImage: `url(${src})`
}"></figure>
</template>
LogoUploader.vue
<template>
<div class="logo-container">
<company-logo size="65px" :src="`${company.logo.url}`"></company-logo>
</div>
<div class="logo-uploaded-details">
<p>Last updated: {company.logo.last_updated}</p>
<button class="file-browse-btn">Upload Image</button>
</div>
</template>
What's happening is that when in company.blade.php I simply have
<logo-uploader></logo-uploader>
The app compiles and loads, however only the CompanyLogo shows up on the screen. The entire markup for the logo-uploaded-details section isn't rendered at all.
I have tried adding a require for the CompanyLogo component to the registration for the LogoUploader component, but that didn't work either.
If I split out the components they both show up. The issue is only once they're nested.
Any ideas?
Vue instances and components must have a single root element. In your LogoUploader you have two root elements.
You need to wrap them in a root.
<template>
<div>
<div class="logo-container">
<company-logo size="65px" :src="`${company.logo.url}`"></company-logo>
</div>
<div class="logo-uploaded-details">
<p>Last updated: {company.logo.last_updated}</p>
<button class="file-browse-btn">Upload Image</button>
</div>
</div>
</template>

aurelia multiple viewPorts on the same component

Is it possible to use viewPorts with the same component, without it being instantiated twice. E.g.
config.map([
{
route: 'route1',
name: 'route1',
viewPorts: {
default: {moduleId: './route1-module'},
heading: {moduleId: './route1-module', view: './route1-module-heading.html'}
},
nav: true,
title: 'Route1'
}]);
route1-module is been instantiated and attached twice. I need to avoid it.
It sounds like you want to use the layouts feature that will be present in a later release (I'm not sure when but the PR has been merged recently).
The PR is here: https://github.com/aurelia/templating-router/pull/25
Essentially it gives you a chance to specify a view/viewmodel pair (a layout) that will sit in place of the original module when routed to. Instead the original content will be projected into the layout using slots.
Example:
route-config
config.map([
{ layoutView: "layout.html", moduleId: 'page1' }
]);
page1.html
<template>
<div slot="slot1">some content</div>
<div slot="slot2">some other content</div>
</template>
layout.html
<template>
<div class="some-fancy-container">
<p>This is slot 2</p>
<!-- slot2 content will be projected here -->
<slot name="slot2">some fallback content</slot>
</div>
<div class="sidebar">
<p>This is slot 1</p>
<!-- slot1 content will be projected here -->
<slot name="slot1">some fallback content</slot>
</div>
</template>
Resulting HTML output:
<template>
<div class="some-fancy-container">
<p>This is slot 2</p>
some other content
</div>
<div class="sidebar">
<p>This is slot 1</p>
some content
</div>
</template>
This is similar to MVC partials or ASP.NET master pages and allows you to specify an alternative layout for certain pages (without needing child routes).
It's very distinct from viewports (it also works with viewports in that you can specify a layout for a viewport too)

Does source binding require a single root element even for non-arrays?

The documentation on source binding has an aside which states:
Important: A single root element should be used in the template when
binding to an array. Having two first level DOM elements will result
in an erratic behavior.
However, I'm finding that this is the case even for non arrays.
I have the following HTML, which sets up two div's populated by two templates. The only difference is that the working template wraps that databound spans in a div.
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2013.3.1119/js/kendo.all.min.js"></script>
<title>JS Bin</title>
<script id="broken-template" type="text/x-kendo-template">
Foo: <span data-bind="text: foo"></span><br/>
Foo Again: <span data-bind="text: foo"></span>
</script>
<script id="working-template" type="text/x-kendo-template">
<div>
Foo: <span data-bind="text: foo"></span><br/>
Foo Again: <span data-bind="text: foo"></span>
</div>
</script>
</head>
<body>
<div id="broken-div" data-template="broken-template" data-bind="source: this">
</div>
<br/>
<br/>
<div id="working-div" data-template="working-template" data-bind="source: this">
</div>
</body>
</html>
And the JavaScript simply creates a view model with a single property and binds it to both divs:
var viewModel = kendo.observable({foo: "bar"});
kendo.bind($("#broken-div"), viewModel);
kendo.bind($("#working-div"), viewModel);
In both cases, only the first root element and it's children are being bound properly. This suggests that every time I databind to template with more than one element I need to make sure it is wrapped in a single root.
Is this behavior documented somewhere? Is there a bug in Kendo or in my sample code? An explanation for why Kendo requires a single root would be great to hear as well.
(Sample code as a jsfiddle)
It's not documented except in the one place you mentioned. Such is the state of Kendo UI documentation - it's less than complete. I've been using Kendo UI for three years and as far as I can tell you, this is its default behavior and not a bug. Unfortunately, it's one of the many quirks you simply learn (stumble upon) from experience.

Sinatra indent partial erb at statement

Is it a way to make partials in sinatra be indented at the level where I call it?
Example
<body>
<div>
<%= partial :"mypartial" %>
</div>
</body>
Results in
<body>
<div>
<div id="i am defined in mypartial">
//etc
</div>
</div>
</body>
When I want
<body>
<div>
<div id="i am defined in mypartial">
//etc
</div>
</div>
</body>
This is possible if I indent the partial currectly, but that makes it hard to work with. I want the partial to be starting indentation all the way to the left(in the source file).
Maybe there is some kind of post processor that can format that html for me?
This is for a project that is not going public, but it's important that internal users can read the generated html easily. Including correct indentation.

Resources