-
-
Save SerafimArts/650b43d830d021ae7c2e to your computer and use it in GitHub Desktop.
import Observable from "Observable"; | |
class AuthController { | |
@Observable | |
user = null; | |
@Observable | |
isAuth = false; | |
constructor() { | |
// Update isAuth if user variable exists | |
this.user.after(value => this.isAuth = value instanceof User); | |
// Remove user if isAuth == false | |
this.isAuth.after(value => { | |
if (!value) { this.user = null; } | |
}); | |
} | |
loginAs(user) { | |
this.user = user; | |
} | |
logout() { | |
this.isAuth = false; | |
} | |
} |
/** | |
* ObservablePrimitive instance | |
*/ | |
class ObservablePrimitive { | |
/** | |
* @constructor | |
* @param value | |
*/ | |
constructor(value = null) { | |
this.events = { | |
before: [], | |
after: [], | |
all: [] | |
}; | |
Object.defineProperty(this, '$value', { | |
enumerable: false, | |
value: value | |
}); | |
} | |
/** | |
* @param value | |
* @return void | |
*/ | |
set value(value) { | |
var exists = typeof value !== "undefined" && value !== null; | |
var oldValue = this.$value; | |
if (exists) { | |
this.events.before.forEach((c) => c(oldValue)); | |
this.$value = value; | |
this.events.all.forEach((c) => c(oldValue, value)); | |
this.events.after.forEach((c) => c(value)); | |
} | |
} | |
/** | |
* Return value | |
* @return {*} | |
*/ | |
get value() { | |
return this.$value; | |
} | |
/** | |
* @param callback | |
* @returns {ObservablePrimitive} | |
*/ | |
after(callback) { | |
this.events.after.push(callback); | |
return this; | |
} | |
/** | |
* @param callback | |
* @returns {ObservablePrimitive} | |
*/ | |
before(callback) { | |
this.events.before.push(callback); | |
return this; | |
} | |
/** | |
* @param callback | |
* @returns {ObservablePrimitive} | |
*/ | |
subscribe(callback) { | |
this.events.all.push(callback); | |
return this; | |
} | |
/** | |
* @returns {*} | |
*/ | |
toString() { | |
return this.value; | |
} | |
/** | |
* @returns {*} | |
*/ | |
[Symbol.toPrimitive]() { | |
return this.value; | |
} | |
} | |
/** | |
* Observable decorator | |
* | |
* @param target | |
* @param key | |
* @param descriptor | |
* @returns {{enumerable, get, set}} | |
* @constructor | |
*/ | |
export default function Observable(target, key, descriptor) { | |
return (function (descriptor) { | |
var value = descriptor.initializer(); | |
var property = new ObservablePrimitive(value); | |
return { | |
enumerable: true, | |
get: () => property, | |
set: (value) => property.value = value | |
}; | |
})(descriptor); | |
} |
Скопировал в песочницу бабеля, что-то не взлетело.
@trikadin у тебя какой-то не такой бабель, вот собрал: http://pastebin.com/mpGait4R
Лол, на оф. сайте бабеля какой-то не такой бабель. Ну ладно.
Зачем такое замыкание внутри декоратора Observable?
Поигрался я ночью с этой штукой (точнее, написал свою). Пока могу сказать, что полноценно сделать именно так, как ты хочешь, не получится -- во-первых, то, что значение -- это всё-таки объект, постоянно откуда-нибудь вылезает. Во-вторых -- бабель очень по-дурацки генерит декораторы для свойств, и просто взять и сделать свойство геттером не получится -- ты, видимо, не проверял, но те геттер и сеттер, что ты возвращаешь из декоратора, падают не на инстанс объекта, а на прототип ObservablePrimitive. Обойти это, увы, можно только грязным хаком. Вот тут можно посмотреть, что я нахимичил, результатом не удовлетворён, но пока с этим ничего не поделать. Думаю, у бабеля ещё будет меняться поведение.
Про твоё решение могу сказать, что у тебя косяк с наследованием (если у свойства нету инициализирующего значения, то оно должно браться из цепочки прототипов).
@trikadin я с офф сайта и собирал, а не отдельно.
Замыкание внутри декоратора нужно что бы ссылка на property всегда относилась к нужному декоратору. Если замыкание убрать, то геттер\сеттер будет обращаться к самой последней декларации обсервабла (я предполагаю, т.к. уже по привычке обрамляю такое в замыкания), а так я просто сохраняю эту ссылку внутри этого замыкания.
Я проверял, но общую работоспособность, а не конкретно этот пример с AuthController. Попробую запилить более приближённый к реальности и выложить куда-нибудь на jsbin.
P.S. Не выкладывай ссылки на babel, они обрезаются, т.к. в get нельзя иметь строку такой длины, как размер кода у тебя.
Не знаю, у тебя не открывается?
А вот так красиво будет в будущем.
Смотреть в лисе, если что, в консоль.
Из плюшек -- возможность отменить присваивание (валидация), возможность сделать один универсальный обработчик хоть для всех инстансов (потому что обработчик получает всю инфу), минимальное потребление памяти.
В общем, прокси классная штука)
@trikadin, это конечно круто, но код по-моему нафига не читаем. Используй нормальное ооп, раз боженька (мозилла и ко) в ES6 его дал =)
А что не читаемо-то? О_о
@trikadin Запускается и работает как надо из под бабела с флагами es7.decorators + es7.objectProperties. =) А зачем нужно... Ну по-моему намного круче писать a = 23, вместо, например a = ko.observable(23) (в случае knockout обсерваблов). Можно сократить и подчистить код в разы.