I am writing code that uses data binding to change the innerHTML of an span to the input of the user, but I can't get it to work. What it should do is show the input on the right side of the input field on both the input fields, but it doesn't. Can someone please help me out.
HTML:
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My Frontend Framework</title>
</style>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" bit-data="name"/>
<span bit-data-binding="name" style="margin-left: 1rem;"></span>
</div>
<div>
<label>Lastname:</label>
<input type="text" bit-data="LastName"/>
<span bit-data-binding="LastName" style="margin-left: 1rem;"></span>
</div>
<script src="frontend-framework.js"></script>
</body>
</html>
Javascript:
const createState = (stateObj) => {
return new Proxy(stateObj, {
set(target, property, value) {
target[property] = value;
render();
return true;
}
});
};
const state = createState({
name: '',
lastName: ''
});
const listeners = document.querySelectorAll('[bit-data]');
listeners.forEach((element) => {
const name = element.dataset.model;
element.addEventListener('keyup', (event) => {
state[name] = element.value;
console.log(state);
});
});
const render = () => {
const bindings = Array.from(document.querySelectorAll('[bit-data-binding]')).map(
e => e.dataset.binding
);
bindings.forEach((binding) => {
document.querySelector(`[bit-data-binding=${binding}]`).innerHTML = state[binding];
document.querySelector(`[bit-data=${binding}]`).value = state[binding];
});
}
https://jsfiddle.net/Mauro0294/g3170whc/4/
I made some changes to the fiddle to get the desired result. The problem was with your logic to refer the elements using the dataset attributes, so I tried to simplify it.
Some notable changes :
Updated the data-bit to use lastName instead of LastName. Made it same as your state.
Used getAttribute to get the value of the data-* properties to correctly get the reference.
I think this is what you're looking for:
const createState = (stateObj) => {
return new Proxy(stateObj, {
set(target, property, value) {
target[property] = value;
render();
return true;
}
});
};
const state = createState({
name: '',
lastName: ''
});
const listeners = document.querySelectorAll('[bit-data]');
listeners.forEach((element) => {
const name = element.getAttribute('bit-data');
console.log('here', element.getAttribute('bit-data'), JSON.stringify(element.dataset))
element.addEventListener('keyup', (event) => {
state[name] = element.value;
console.log(state);
});
});
const render = () => {
const bindings = Array.from(document.querySelectorAll('[bit-data-binding]')).map((e) => {
return e.getAttribute('bit-data-binding');
});
//console.log('bindings:', bindings, document.querySelectorAll('[bit-data-binding]'));
(bindings ?? []).forEach((binding) => {
document.querySelector(`[bit-data-binding=${binding}]`).innerHTML = state[binding];
document.querySelector(`[bit-data=${binding}]`).value = state[binding];
});
}
<html lang="en-US">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My Frontend Framework</title>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" bit-data="name"/>
<span bit-data-binding="name" style="margin-left: 1rem;"></span>
</div>
<div>
<label>Lastname:</label>
<input type="text" bit-data="lastName"/>
<span bit-data-binding="lastName" style="margin-left: 1rem;"></span>
</div>
</body>
</html>
Your main issue is this part:
const bindings = Array.from(document.querySelectorAll('[bit-data-binding]')).map(
e => e.dataset.binding
);
or more specifically e.dataset.binding. Your elements do not a have data-binding attribute, which would be the prerequisite for using dataset.binding. You can use e.getAttribute('bit-data-binding') instead.
But your logic is also flawed: As it currently stands, entering text into an input is pointless, as the state is never updated.
Finally, note that you spell LastName with a capital L in your DOM but lowercased in your state object.
Related
I, not so long ago, went ahead and built an html dependent dropdown which pulls it's data from an array in the js. The dependencies worked perfectly fine until I realized that I needed to add a search function to the dropdown.
I went through different alternatives and to me the simplest option was to use select2 plugin. The problem I am having is that when using select2, it doesn't seem to be triggering the EventListener (Line 43 in JS) I had previously setup for the regular select.
Find below what I have attempted:
HTML:
<!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">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/select2#4.0.13/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2#4.0.13/dist/js/select2.min.js"></script>
<title>Document</title>
</head>
<body>
<select id ="level1" style='width: 300px;'></select>
<select id ="level2" style='width: 300px;'></select>
<select id ="level3" style='width: 300px;'></select>
<hr>
<select id ="level4" disabled></select>
<select id ="level5" disabled></select>
<select id ="level6" disabled></select>
<select id ="level7" disabled></select>
<hr>
<h1 id ="level8"></h1>
<script src="betterdd.js"></script>
</body>
</html>
JS: (Select options are found in var myData = [...])
class DropDown {
constructor(data){
this.data = data;
this.targets = [];
}
filterData(filtersAsArray){
return this.data.filter(r => filtersAsArray.every((item,i) => item === r[i]));
}
getUniqueValues(dataAsArray,index){
const uniqueOptions = new Set();
dataAsArray.forEach(r => uniqueOptions.add(r[index]));
return [...uniqueOptions];
}
populateDropDown(el,listAsArray){
el.innerHTML = "";
listAsArray.forEach(item => {
const option = document.createElement("option");
option.textContent = item;
el.appendChild(option);
});
}
createPopulateDropDownFunction(el,elsDependsOn){
return () => {
const elsDependsOnValues = elsDependsOn.length === 0 ? null : elsDependsOn.map(depEl => depEl.value);
const dataToUse = elsDependsOn.length === 0 ? this.data : this.filterData (elsDependsOnValues);
const listToUse = this.getUniqueValues(dataToUse, elsDependsOn.length);
this.populateDropDown(el,listToUse);
}
}
add(options){
//{target: "level2", dependsOn: ["level1"] }
const el = document.getElementById(options.target);
const elsDependsOn = options.dependsOn.length === 0 ? [] : options.dependsOn.map(id => document.getElementById(id));
const eventFunction = this.createPopulateDropDownFunction (el, elsDependsOn);
const targetObject = { el: el, elsDependsOn: elsDependsOn,func: eventFunction};
targetObject.elsDependsOn.forEach(depEl => depEl.addEventListener("change",eventFunction));
this.targets.push(targetObject);
return this;
}
initialize(){
this.targets.forEach(t => t.func());
return this;
}
eazyDropDown(arrayOfIds){
arrayOfIds.forEach((item,i) =>{
const option = {target: item, dependsOn: arrayOfIds.slice(0,i) }
this.add(option);
});
this.initialize();
return this;
}
}
var dd = new DropDown(myData).eazyDropDown(["level1","level2","level3","level4","level5","level6","level7","level8"])
add the following line inside add method :
const eventFunction = this.createPopulateDropDownFunction (el, elsDependsOn);
el.addEventListener("change", (e) => {
eventFunction();
console.log(e.target.value)
})
and remove the following line:
targetObject.elsDependsOn.forEach(depEl => depEl.addEventListener("change",eventFunction));
I'm trying to make a web framework and one feature will be a key-value state management tool. I need the second <script> tag to only run after ./script.js loads in.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./framework.js"></script>
</head>
<body>
<p f-text="name"></p>
<script>
Framework.store('name', 'Joe');
</script>
</body>
</html>
framework.js:
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text')) {
const textValue = element.getAttribute('f-text');
const key = window.fStore.find((x) => x.key === textValue);
element.innerHTML = key.value;
}
});
window.Framework = {
store: (key, value?) => {
if (!value) {
const foundKey = window.fStore.find((x) => x.key === key);
return foundKey.value;
}
window.fStore = [...window.fStore, { key: key, value: value }];
}
}
Error:
SyntaxError: Unexpected token ')'
at /framework.js:12:22
ReferenceError: Framework is not defined
at /:12:5
You need to wait that your script is loaded, you can use this
window.addEventListener('load', function() {
Framework.store('name', 'Joe');
})
I have this code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./framework.js"></script>
</head>
<body>
<p f-text="name"></p>
<script>
Framework.store('name', 'Joe');
</script>
</body>
</html>
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text')) {
const textValue = element.getAttribute('f-text');
element.innerHTML = window.fStore[textValue];
}
});
window.Framework = {
store: (key, value = '') => {
if (value === '') {
return window.fStore[key];
}
window.fStore[key] = value;
}
}
But get this error:
TypeError: Cannot set properties of undefined (setting 'name')
at Object.store (/framework.js:15:24)
at /:12:15
I want the page to render 'Joe' by getting the key from f-text, finding the key's value in window.fStore, then setting the element.innerHTML as the value. Framework.store() takes a key and a value, if there is no value it returns the value from the key in window.fStore, if there is then it sets window.fStore[key] to the value.
You need to check whether window.fStore exists first.
window.Framework = {
store: (key, value = '') => {
if(!window.fStore) window.fStore = {}
if (value === '') {
return window.fStore[key];
}
window.fStore[key] = value;
}
}
Framework.store('name', 'Joe');
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text')) {
const textValue = element.getAttribute('f-text');
element.innerHTML = window.fStore[textValue];
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p f-text="name"></p>
</body>
</html>
You might also need to wait till the window loads first. Some browsers will give you a headache if you dont
window.addEventListener('load', e=>{
window.Framework = {
store: (key, value = '') => {
if(!window.fStore)
window.fStore = {};
if (value === '')
return window.fStore[key];
window.fStore[key] = value;
}
}
window.Framework.store('name', 'Joe');
document.querySelectorAll('*').forEach((element) => {
if (element.hasAttribute('f-text'))
element.innerHTML = window.fStore[element.getAttribute('f-text')];
});
}
im new in js
I want to make a code with onekeyup to search an object in an array. I don't know how to do it with one/specific parameter, not all of the parameter.
for ex. If I type tokyo, then it will not show anything because its not "name" parameter
script.js and index.html
function checkOnKeyUp(input){
const filtered = datas.filter(element => {
for (const value of Object.values(element)) {
if (
value.toString()
.toLowerCase()
.includes(input.value.toLowerCase())
)
return true;
}
})
console.log('Name: ', filtered);
document.getElementById("result").innerText=JSON.stringify(filtered)
}
const datas = [{
"nickname":"abi","name":"Abi sholeh","id":123,"birth":"1999-05-09","address":"new york"},{"nickname":"abc","name":"abc james","id":112,"birth":"1999-05-04","address":"tokyo"}];
<!DOCTYPE html>
<html lang="en">
<head>
<title>Pengenalan Javascript</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<input type="text" id='search' name='search' onkeyup="checkOnKeyUp(this)">
<div id="result"></div>
<script src="./script.js"></script>
</body>
</html>
thanks
I think you are trying to the follow:
datas.map(data => Object.values(data))
.flat()
.filter(value =>
value.toString()
.toLowerCase()
.includes(input.value.toLowerCase()
)
.forEach(data => {
data = 'nama';
});
Maybe this is what you are looking for:
function checkOnKeyUp(input){
let value = input.value.toLowerCase();
let filtered = datas.filter(element => {
return element.name.toLowerCase() === value;
})
console.log('Name: ', filtered.name);
document.getElementById("result").innerText=JSON.stringify(filtered)
}
i am fetching some data from the server and based on that i am initialising the ng-value of input which is shown after the data is fetched. But after that when I call a function with ng-change, console log doesn't show the changed value of the ng-model="chosedOption". It continues to print "Paytm Blance" even after selecting other radio buttons.
angular js file
var app = angular.module("formModule", []);
var formController = ($scope, $http) => {
$scope.chosedOption = "Paytm Balance";
$scope.amount = 10;
$scope.resJson = "";
$scope.payOptions = undefined;
$scope.proceed = (chosedOption) => {
console.log(chosedOption);
console.log($scope.chosedOption);
}
$scope.loadDoc = () => {
const orderId = Math.floor(Math.random() * 10000) + 100000;
const data = {
amount: $scope.amount,
orderId: orderId
}
$http.post("http://localhost:3200/intiate_transaction_api",data)
.then(response => {
$scope.resJson = JSON.stringify(response, undefined, 4);
console.log(response);
let data = {
txnToken: response.data.body.txnToken,
orderId: orderId
}
$http.post('http://localhost:3200/fetch_payment_option_api', data)
.then(response2 => {
$scope.resJson = JSON.stringify(response2, undefined, 4);
$scope.payOptions = response2.data.body.merchantPayOption.paymentModes;
})
})
}
}
app.controller("formController", formController);
app.filter('safeHtml', $sce => {
return function(val) {
return $sce.trustAsHtml(val);
}
})
html file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="./style.css">
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.5/angular.min.js"></script>
<title>UI</title>
</head>
<body ng-app="formModule" ng-controller="formController">
<div class="result">
<div class="container">
<form name="amt" action="#"></form>
total amount<input type="text" id="amount" ng-model="amount" name="amount">
<button ng-click="loadDoc()">Pay</button>
</div>
</div>
<div id="payOtions" ng-if="payOptions">
<div ng-repeat="option in payOptions">
<label>
<div class="paymentOption" ng-class="">
<input name="payOption"
ng-change="proceed(option.displayName)"
ng-model="chosedOption" type="radio"
ng-value="option.displayName">
{{option.displayName}}
</div>
</label>
</div>
</div>
<pre><code id="demo" ng-bind-html="resJson | safeHtml"></code></pre>
<script src="./angularScript.js"></script>
</body>
</html>
output
can be checked here
The ng-change directive only listens to changes made by the user. It does not listen to changes made by the controller.
Any changes done by the controller should call the listening function from the controller.