Closed pjhjohn closed 5 years ago
CSS로 구현하기 https://jsfiddle.net/jmo5tp9f/
<input type="email" minlength="4" required="required" />
<span class="msg">Email is required</span>
<input type="password" minlength="4" required="required" />
<span class="msg">Password is too short</span>
input {
display: block;
}
input:valid + span.msg {
display: none;
}
input:not(:valid) + span.msg {
display: initial;
}
http://tech.kakao.com/2017/01/09/daummovie-rxjs/ 요거가 예시가 참 좋군요
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Reactive X Count</title>
<style>
.hidden {
display: none;
}
.error-msg {
color: red;
}
</style>
</head>
<body>
<div>
<label for="email">Email</label>
<input id="email" type="email" placeholder="Email" pattern=".*(\.\w+)$" required>
<p id="email-error" class="error-msg hidden"></p>
</div>
<div>
<label for="password">Password</label>
<input id="password" type="password" placeholder="Password" minlength="8" maxlength="64" required>
<p id="password-error" class="error-msg hidden" ></p>
</div>
<div>
<button id="submit" class="hidden">Submit</button>
<p id="result-msg" class="hidden"></p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.min.js"></script>
<script>
const submitBtn = document.getElementById("submit");
const inputs = document.getElementsByTagName("input");
const inputArray = Array.from(inputs);
const resultMsg = document.getElementById("result-msg");
const registerValidation = (inputId) => {
const input = document.getElementById(inputId);
Rx.Observable
.fromEvent(input, "keyup")
.debounce(1000)
.map((e) => {
const valid = inputArray.every(input => input.validity.valid);
const showBtn = inputArray.every(input => input.value);
if (showBtn) {
submitBtn.classList.remove("hidden");
}
submitBtn.disabled = !valid;
return e;
})
.map((e) => e.target)
.forEach((target) => {
const errorEl = document.getElementById(`${inputId}-error`);
const errorClassList = errorEl.classList;
if (target.validity.vaild) {
errorClassList.add("hidden");
} else {
if(target.validity.patternMismatch) {
errorEl.textContent = "올바른 이메일 형식이 아닙니다."
} else {
errorEl.textContent = target.validationMessage;
}
errorClassList.remove("hidden");
}
});
};
registerValidation("email");
registerValidation("password");
Rx.Observable
.fromEvent(submitBtn, "click")
.map(() => {
console.log("map");
inputArray.forEach(input => {
input.disabled = true;
});
submitBtn.disabled = true;
})
.flatMapFirst(Rx.Observable.fromPromise( () =>
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("asrtonuyasrtuyon")
const d = new Date();
if (d.getTime() % 2 === 0) {
resolve(true);
} else {
reject(false);
}
}, 5000);
})
).take(1))
.subscribe(
onNext = (result) => {
activateInputsAndButtons();
resultMsg.textContent = "이야 드디어 내가 해냈어!";
resultMsg.classList.remove("hidden");
},
onError = (err) => {
resultMsg.textContent = "아닛 내가 실패라니 그게 무슨소리오!";
resultMsg.classList.remove("hidden");
},
onCompleted = () => {
activateInputsAndButtons();
}
);
const activateInputsAndButtons = () => {
inputArray.forEach(input => {
input.disabled = false;
});
submitBtn.disabled = false;
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Reactive X Count</title>
<style>
.hidden {
display: none;
}
.error-msg {
color: red;
}
</style>
</head>
<body>
<div>
<label for="email">Email</label>
<input id="email" type="email" placeholder="Email" pattern=".*(\.\w+)$" required>
<p id="email-error" class="error-msg hidden"></p>
</div>
<div>
<label for="password">Password</label>
<input id="password" type="password" placeholder="Password" minlength="8" maxlength="64" required>
<p id="password-error" class="error-msg hidden" ></p>
</div>
<div>
<button id="submit" class="hidden">Submit</button>
<p id="result-msg" class="hidden"></p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.min.js"></script>
<script>
const emailInput = document.getElementById("email");
const passwordInput = document.getElementById("password");
const submitBtn = document.getElementById("submit");
const resultMsg = document.getElementById("result-msg");
const networkStateIdle = "IDLE";
const networkStatePending = "PENDING";
const networkStateSubject = new Rx.BehaviorSubject(networkStateIdle);
const observeValidationResultOf = (input) => {
return Rx.Observable
.fromEvent(input, "keyup")
.debounce(1000)
.map((e) => {
const valid = input.validity.valid
const target = e.target
const errorEl = document.getElementById(`${input.id}-error`);
const errorClassList = errorEl.classList;
if (valid) {
errorClassList.add("hidden");
} else {
if(target.validity.patternMismatch) {
errorEl.textContent = "올바른 이메일 형식이 아닙니다."
} else {
errorEl.textContent = target.validationMessage;
}
errorClassList.remove("hidden");
}
return valid;
})
}
// Submit Button Click => Submit Button Enablability 결정 (서버다녀옴)
Rx.Observable
.fromEvent(submitBtn, "click")
.doOnNext(() => {
networkStateSubject.onNext(networkStatePending);
})
.flatMap(Rx.Observable.fromPromise(() =>
new Promise((resolve, reject) => {
setTimeout(() => {
console.log("asrtonuyasrtuyon")
const d = new Date();
if (d.getTime() % 2 === 0) {
resolve(true);
} else {
reject(false);
}
}, 5000);
})
).take(1))
.subscribe(
onNext = (result) => {
networkStateSubject.onNext(networkStateIdle);
resultMsg.textContent = "이야 드디어 내가 해냈어!";
resultMsg.classList.remove("hidden");
},
onError = (err) => {
networkStateSubject.onNext(networkStateIdle);
resultMsg.textContent = "아닛 내가 실패라니 그게 무슨소리오!";
resultMsg.classList.remove("hidden");
},
onCompleted = () => {
networkStateSubject.onNext(networkStateIdle);
}
);
// Submit Button Enablability
Rx.Observable.combineLatest(
observeValidationResultOf(emailInput), // Action Dispather
observeValidationResultOf(passwordInput), // Action Dispather
networkStateSubject, // Action Dispather
(emailIsValid, passwordIsValid, networkState) => {
// Reducer
return emailIsValid && passwordIsValid && networkState === networkStateIdle;
}
).subscribe(
onNext = (valid) => {
// State Update
submitBtn.classList.remove("hidden");
submitBtn.disabled = !valid;
}
)
// Email & Password Input Enability
networkStateSubject.subscribe(
onNext = (networkState) => {
const disabled = networkState === networkStatePending;
emailInput.disabled = disabled;
passwordInput.disabled = disabled;
}
);
</script>
</body>
</html>
Goal
ReactiveX 를 이용하여 여러 Observable / Operator 를 사용해보도록 하자. 어떤 Observable / Operator 를 사용하는지는 과제의 요구사항에 있지는 않으니 자유롭게 구현하면 된다. 각 항목에서 자유롭게 심화 구현을 해도 된다.
Task
Email & Password 를 입력받아 로그인 시도를 흉내내는 UI를 만든다.
화면 구성 : 총 6개 View
동작
aaa@bbb.com
형식이 아니라면 올바른 형식 아님 에러 메시지를 띄운다