Skip to content

Instantly share code, notes, and snippets.

@padawanR0k
Created September 4, 2021 04:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save padawanR0k/dac1ee2b621361b8b259a91563d05280 to your computer and use it in GitHub Desktop.
Save padawanR0k/dac1ee2b621361b8b259a91563d05280 to your computer and use it in GitHub Desktop.
vanilla javscript component with pubSub example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="App">
</div>
<script>
class Component {
constructor({ selector, attr, children, template, tag, store, parent }) {
if (selector) {
/** @type {HTMLElement} */
this.$el = document.querySelector(selector);
}
if (tag) {
this.$el = document.createElement(tag);
}
if (parent) {
this.parent = parent;
}
if (store) {
/** @type {object} */
this.store = new PusSub(store);
}
if (this.attr) {
/** @type {object} */
this.attr = attr;
for (let key in this.attr) {
this.$el.setAttribute(key, this.attr[key]);
}
}
/** @type {Component} */
this.parent = null;
/** @type {Component[]} */
this.children = children;
this.template = template;
this.mount();
}
mount() {
this.render(this.template);
this.didMount();
}
didMount() {
}
getStore() {
if (this.store) {
return this.store;
} else if (this.parent) {
return this.parent.getStore();
} else {
return null;
}
}
render(html) {
if (html && this.$el.innerHTML !== html) this.$el.innerHTML = html;
if (this.children) {
this.children.map(([Component, params]) => {
params.parent = this;
const child = new Component(params);
// child.store = this.store;
const that = this;
child.parent = that;
this.$el.appendChild(child.$el);
child.mount();
});
}
}
}
class PusSub {
constructor(data) {
this.data = data;
this.callbacks = {};
}
pub(key) {
if (this.callbacks[key]) {
this.callbacks[key].map(cb => cb(this.data[key]));
}
}
sub(key, cb) {
this.callbacks[key] = this.callbacks[key] ? [...this.callbacks[key], cb] : [cb];
}
}
class App extends Component {
constructor(params) {
super(params);
}
}
class Header extends Component {
constructor(params) {
super(params);
}
}
class Main extends Component {
constructor(params) {
super(params);
}
didMount() {
const store = this.getStore();
if (store) {
store.sub('counter', (counter) => console.log(counter.count));
}
}
}
class Counter {
#count;
constructor(cnt) {
this.count = cnt;
}
get count() {
return this.#count;
}
set count(cnt) {
this.#count = cnt;
}
add() {
this.count = this.count + 1;
}
subtract() {
this.count = this.count - 1;
}
}
class CounterComp extends Component {
constructor(params) {
super(params)
this.$cnt = this.$el.querySelector('.cnt');
this.$el.addEventListener('click', (e) => {
const {target} = e;
if (target.tagName === 'BUTTON') {
if (target.classList.value === 'add') {
this.getStore().data.counter.add()
} else {
this.getStore().data.counter.subtract();
}
this.getStore().pub('counter')
this.$cnt.innerHTML = this.getStore().data.counter.count;
}
})
}
}
class Footer extends Component {
constructor(params) {
super(params)
}
}
const app = new App({
selector: '.App',
attr: {},
store: {
counter: new Counter(0)
},
children: [
[Header, {
tag: 'header',
attr: { id: 'header'},
template: '<h2>header</h2>'
}],
[Main, {
tag: 'main',
attr: { id: 'main'},
template: '<main>main</main>',
children: [
[CounterComp, {
tag: 'div',
attr: { id: 'counter' },
template: `
<button class="subtract">-</button>
<span class="cnt">0</span>
<button class="add">+</button>
`,
}]
]
}],
[Footer, {
tag: 'footer',
attr: { id: 'footer'},
template: '<h2>footer</h2>'
}],
],
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment