A Pen by Marcus Obst on CodePen.
-
-
Save marcus-at-localhost/68cbf0e637a39ddeca59199b717d46cb to your computer and use it in GitHub Desktop.
/* | |
Alpine Dev Tools: https://github.com/Te7a-Houdini/alpinejs-devtools | |
*/ | |
function gistsData() { | |
return { | |
title: 'Latest Gists', | |
gists: [], | |
reload() { | |
sessionStorage.removeItem("gists"); | |
this.gists = []; | |
this.init(); | |
}, | |
init() { | |
// Testdata, in case I hit my 60 calls per hour | |
/*let gists = [ | |
{ | |
"id": "8f6af49ffe693c15faca67a7f3bf1a31", | |
"html_url": "https://gist.github.com/8f6af49ffe693c15faca67a7f3bf1a31", | |
"description": "Test" | |
} | |
];*/ | |
// Check if sessionData holds anything, so we don't need to hit the api | |
const gists = JSON.parse(sessionStorage.getItem("gists")); | |
if(gists){ | |
// make it accessible to x-data | |
this.gists = gists; | |
console.log('sessionStorage', gists); | |
return; | |
} | |
console.log(new leptonParser().parse('[bla] #blub')) | |
// get gists and parse the description field | |
fetch('https://api.github.com/users/marcus-at-localhost/gists') | |
.then(response => response.json()) | |
.then(response => { | |
console.log('fetched',response); | |
// I could use collect.js to manipulate the response further. | |
let gists = response.map((item) => { | |
// parser: https://codepen.io/localhorst/pen/ZEbqVZd | |
item.parsed = new leptonParser().parse(item.description); | |
return item; | |
}); | |
this.gists = gists; | |
sessionStorage.setItem("gists",JSON.stringify(gists)); | |
//console.log(this,response) | |
}); | |
} | |
}; | |
} |
<div | |
class="h-full bg-gray-200 text-gray-800 p-4 lg:p-8" | |
x-data="gistsData()" | |
x-init="init()" | |
> | |
<header class="flex items-center"> | |
<h1 class="flex-grow" x-text="title"></h1> | |
<button | |
class="py-2 px-4 rounded bg-blue-500 text-white flex-grow-0" | |
type="button" | |
x-on:click="reload()" | |
> | |
Reload | |
</button> | |
</header> | |
<ul class="list-reset flex flex-col"> | |
<template x-for="gist in gists" :key="gist.id"> | |
<li class="relative -mb-px block border p-4 border-grey"> | |
<a x-bind:href="gist.html_url" x-text="gist.parsed.title"></a><br> | |
<small x-text="gist.parsed.description"></small> | |
</li> | |
</template> | |
</ul> | |
<p class="mt-3 text-xs"> | |
<a href="https://developer.github.com/v3/gists/">API</a> · | |
<a href="https://github.com/alpinejs/alpine">Alpine.js</a> · | |
<a href="https://tailwindcss.com/docs">Tailwind</a> · | |
<a href="https://tailwindcomponents.com/component/bootstrap-4-list-group-clone">Tailwind Components</a> · | |
<a href="https://github.com/hackjutsu/Lepton">Lepton Snipped Management</a> | |
</p> | |
</div> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/1.4.6/tailwind.min.css" rel="stylesheet" /> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/collect.js/4.25.0/collect.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/alpine.min.js"></script> | |
<script src="leptonParser.js"></script> | |
<script src="app.js"></script> | |
// borrowed from Lepton | |
// @link https://github.com/hackjutsu/Lepton/blob/0e6d75047c/app/utilities/parser/index.js | |
class leptonParser { | |
//constructor | |
constructor(payload='') { | |
this.payload = payload; | |
} | |
parse(payload){ | |
this.payload = payload || 'No description'; | |
return this.descriptionParser(payload); | |
} | |
descriptionParser (payload) { | |
const rawDescription = payload; | |
const regexForTitle = rawDescription.match(/\[.*\]/); | |
const rawTitle = (regexForTitle && regexForTitle[0]) || ''; | |
const title = ((rawTitle.length > 0) && rawTitle.substring(1, regexForTitle[0].length - 1)) || rawDescription; | |
let tagStyle = 'legacy'; | |
let customTags = this.parseCustomTagsLegacy(rawDescription); | |
if (customTags.length === 0) { | |
customTags = this.parseCustomTagsTwitter(rawDescription); | |
tagStyle = customTags.length > 0 ? 'twitter' : 'legacy'; | |
} | |
const descriptionLength = tagStyle === 'legacy' | |
? rawDescription.length - customTags.length | |
: rawDescription.length; | |
const description = rawDescription.substring(rawTitle.length, descriptionLength); | |
return { title, description, customTags }; | |
} | |
parseCustomTagsLegacy (payload) { | |
const regextForCustomTags = payload.match(/#tags:.*$/); | |
const customTags = (regextForCustomTags && regextForCustomTags[0]) || ''; | |
return customTags; | |
} | |
// http://geekcoder.org/js-extract-hashtags-from-text/ | |
parseCustomTagsTwitter (payload) { | |
var regex = /(?:^|\s)(?:#)([a-zA-Z\d]+)/gm; | |
var matches = []; | |
var match; | |
while ((match = regex.exec(payload))) { | |
matches.push(match[1]); | |
} | |
if(!matches.length){ | |
return ''; | |
} | |
return matches.reduce((acc, cur) => acc + ', ' + cur); | |
} | |
} | |
console.log(new leptonParser().parse('[bla] #blub #foo and #bar')); |
Ok, I deleted my previous answer, because it was a lot of guessing :)
The script should work with the latest Alpine version, I just checked it.
Maybe and I still have no real understanding how those arrow function work and how the fancy new JS syntax affects the scope, but try to change your function to:
function TimerWidget(){}
// or
const TimerWidget = () => {}
to get the Alpine Proxy:{}
thing with this
.
Thanks for the reply!
I tried your tip already, and the result was the same, but... actually it was almost that, and you helped me to find the solution:
{ initialize: () => {} }
is not the same as { initialize() {} }
BAD:
const TimerWidget = {
loading: true,
initialize: () => {
this; // <- window
}
}
GOOD:
const TimerWidget = {
loading: true,
initialize() {
this; // <- Proxy { <target>: {}, <handler>: {…} }
}
}
Thanks again for the gist and for your help! 🙇
I'm glad it worked out! See, these 5000 ways to declare functions/methods in JS and how they are scoped, that's just weird :). I have no idea how this {fun(){},foo: 'bar'}
is possible. But it was on my plate to read up about that anyway - so thanks for bringing that up.
Hey Marcus, thanks for this gist. This is exactly what I'm trying to achieve.
Does this work with the latest Alpine version (2.8.1)?
For me, in particular, this line doesn't work: https://gist.github.com/marcus-at-localhost/68cbf0e637a39ddeca59199b717d46cb#file-app-js-L48
Accessing
this
from withinthen()
gives backwindow
instead of the data object.Do you see anything obvious I might be doing wrong here?