How to bind data to multiple elements using javascript - javascript

I need to implement one-way data binding in multiple places using a JSON object.
Below is the JSON object getting from the server every 10 sec, with the latest status of a user.
<html>
<head></head>
<body>
<ul>
<li>User0: <span class="change_status" unique_id="7150032">Busy</span></li>
<li>User1: <span class="change_status" unique_id="7150031">Offline</span></li>
<li>User2: <span class="change_status" unique_id="7150030">Online</span></li>
</ul>
<div class="profile">
<div class="profileimage">
U
</div>
Status: <span class="change_status" unique_id="7150032">Busy</span>
</div>
<script>
function Binding(b) {
_this = this
this.elementBindings = []
this.value = b.object['unique_id']
this.valueGetter = function(){
return _this.value;
}
this.valueSetter = function(val){
_this.value = val
for (var i = 0; i < _this.elementBindings.length; i++) {
var binding=_this.elementBindings[i]
binding.element[binding.attribute] = val
}
}
this.addBinding = function(element, unique_id){
var binding = {
element: element,
unique_id: unique_id
}
this.elementBindings.push(binding)
element['innerText'] = _this.value
return _this
}
Object.defineProperty(b.object, b.property, {
get: this.valueGetter,
set: this.valueSetter
});
b.object[b.property] = this.value;
}
obj = [
{
"unique_id": "7150032",
"status_name": "Busy"
},
{
"unique_id": "7150031",
"status_name": "Offline"
},
{
"unique_id": "7150030",
"status_name": "Online",
}
]
var changeStatusElements = document.getElementsByClassName("change_status")
var binding = new Binding({
object: obj
})
for(var i=0; i< changeStatusElements.length; i++){
var unique_id = changeStatusElements[i]['unique_id']
binding.addBinding(changeStatusElements[i], unique_id)
}
</script>
</body>
</html>
These statuses will change based on the new JSON that has new statuses. Please assume the JSON (contains a live status of the users) will change every 10 sec.
Please correct my code that would be appreciated.

Give your ul element an id attribute, so you can reference it from code. I will assume it is called "container". Initially it should just be an empty element, like
<ul id="container"></ul>
You should also have a URL where to make the request to. I'll assume you have that in a variable url.
Then, you can add this code:
(async function request() {
let response = await fetch(url);
let { data } = await response.json();
document.querySelector("#container").innerHTML = data.map(({ unique_id, status_name }, i) =>
`<li>User${i}: <span class="change_status" unique_id="${unique_id}">${status_name}</span></li>`).join("\n");
setTimeout(request, 10000);
})();

I have done this. Please check the following code
function Binding(b) {
_this = this
this.statusArrProxy = b[b.property]
this.statusGetter = function () {
return _this.statusArrProxy;
}
this.statusSetter = function (arr) {
_this.statusArrProxy = arr
arr.forEach(a => {
var elements = document.querySelectorAll('.change_status[' + b.key + '="' + a[b.key] + '"]')
elements.forEach(node => node[b.attribute] = a[b.value])
})
}
Object.defineProperty(b, b.property, {
get: this.statusGetter,
set: this.statusSetter
})
b[b.property] = this.statusArrProxy
}
var statusArray = [{ unique_id: 123, status: 'Busy' }, { unique_id: 1234, status: 'Available' }]
var bindingObj = {
statusArray: statusArray,
attribute: 'innerHTML',
property: 'statusArray',
key: 'unique_id',
value: 'status'
}
new Binding(bindingObj)
//replace the entire array bindingObj.statusArray = [{ unique_id: 123, status: 'Offline' }, { unique_id: 1234, status: 'Out Of Office' }]
thank you for all support

Related

click event listener not stiking . Vanila js

I've created a store with a list of products that are generated from js and i attached an event listener to every product.
For sorting purposes, i've decided to recreate the dom to put the products in the order that i want but the problem is that the click event doesnt work and i dont know why. My line of thinking is that if something is declared globally, it should be accesable from all corners of the aplication. Am i right?
const grid = document.querySelector('.grid');
//arr of products
const productsArr = [{
name: 'Aname1',
price: 200
},
{
name: 'Cname2',
price: 2000
},
{
name: 'Zname3',
price: 28
},
{
name: 'Pname4',
price: 5
}
];
const paintProducts = function() {
productsArr.forEach(product, () => {
let price = product.price;
let name = product.name;
//create html
productsHtml = `<div product data-price="${price}" data-name = "${name}">
<h6 class="price">${price} coco</h6>
<p class="product-descr">${description}</p>
</div>`
});
//insert html
grid.insertAdjacentHTML('beforeend', productsHtml);
};
paintProducts();
// filter
const aZBtn = document.querySelector('.a-z');
const filterAlphabetically = () => {
allInstruments.sort(function(i, j) {
if (i.name < j.name) {
return -1;
}
if (i.name > j.name) {
return 1;
}
return 0;
});
};
//clean the dom
const cleanGrid = function() {
grid.innerHTML = '';
};
aZBtn.addEventListener('click', function() {
filterAlphabetically();
cleanGrid();
paintProducts();
clickOnProduct();
});
const products = document.querySelectorAll(".product")
const clickOnProduct = function() {
products.forEach(function(product) {
product.addEventListener('click', () => {
console.log("something here")
})
})
}
<div class="grid-container">
<div class="a-z">Alphabetically</div>
<div class="grid">
</div>
</div>
First I found a logical flaw in paintProducts
The following line was outside the loop, so only 1 item could ever be rendered, the last one.
grid.insertAdjacentHTML('beforeend', productsHtml);
Second was in filterAlphabetically. There a mistery variable pops up allInstruments, replaced it with productsArr.
Third problem was const products = document.querySelectorAll(".product"). This was outside the clickOnProduct function.
As every repaint generates new DOM elements, after sorting, the events need to be bound to the new elements.
Fourth problem is the product div itself, it countained <div product ... but you are using a queryselector referring a class so this should be <div class="product" ...
When fixing all the above, we result in this:
<html>
<body>
<div class="grid-container">
<div class="a-z">Alphabetically</div>
<div class="grid">
</div>
</div>
</body>
<script>
const grid = document.querySelector('.grid');
//arr of products
const productsArr = [{
name: 'Aname1',
price: 200
},
{
name: 'Cname2',
price: 2000
},
{
name: 'Zname3',
price: 28
},
{
name: 'Pname4',
price: 5
}
];
const paintProducts = function() {
productsArr.forEach(product => {
let price = product.price;
let name = product.name;
//create html
productsHtml = `<div class="product" data-price="${price}" data-name = "${name}">
<h6 class="price">${price} coco</h6>
<p class="product-descr">${name}</p>
</div>`
//insert html
grid.insertAdjacentHTML('beforeend', productsHtml);
});
};
paintProducts();
// filter
const aZBtn = document.querySelector('.a-z');
const filterAlphabetically = () => {
productsArr.sort(function(i, j) {
if (i.name < j.name) {
return -1;
}
if (i.name > j.name) {
return 1;
}
return 0;
});
};
//clean the dom
const cleanGrid = function() {
grid.innerHTML = '';
};
aZBtn.addEventListener('click', function() {
filterAlphabetically();
cleanGrid();
paintProducts();
clickOnProduct();
});
const clickOnProduct = function() {
var products = document.querySelectorAll(".product")
products.forEach(function(product) {
product.addEventListener('click', () => {
console.log("something here")
});
});
}
clickOnProduct()
</script>
</html>

Add elements from array to object to format fusionchart data

I want to format the data of my fusion chart based on scope variable.
I have a function which gets dates and stock values assigned to this dates.
So I have 2 arrays:
dates = [2017-04-28, 2017-04-27, 2017-04-26, 2017-04-25]
stockValues = [150.25, 147.7, 146.56, 146.49]
What I want to do is to create a new object which looks like this:
data: [{
"label": "2017-04-28",
"value": "150.25"
},
{
"label": "2017-04-27",
"value": "147.7"
},
... //and so on
]
I managed to come up with following code:
$scope.getStockData = function(stockID) {
$http.get('/stock', {
params : {
stockID : encodeURI(stockID)
}
}).then(function(response) {
$scope.stock = response.data;
var data={};
$scope.data={};
angular.forEach(response.data.dates,function(value){
data["label"] = value;
})
angular.forEach(response.data.stockValues,function(value){
data["value"] = value;
})
$scope.data = data;
}, function(response) {
$scope.showError = true;
}).finally(function() {
});
};
The problem is that this solution creates object which looks like this:
{"label":"2017-04-25","value":"146.49"}
So it takes only the last values from array.
How can I make my object look the way I want it to?
Example:
const dates = ['2017-04-28', '2017-04-27', '2017-04-26', '2017-04-25']
const stockValues = ['150.25', '147.7', '146.56', '146.49']
const r = dates.map((d, i) => Object.assign({
label: d,
value: stockValues[i]
}))
console.log(JSON.stringify(r, null, 2))
Try this, you must initialize an array, and the push at the right location.
$scope.getStockData = function(stockID) {
$http.get('/stock', {
params : {
stockID : encodeURI(stockID)
}
}).then(function(response) {
$scope.stock = response.data;
var data=[];
$scope.data=[];
angular.forEach(response.data.dates,function(value, i){
data[i]["label"] = value;
})
angular.forEach(response.data.stockValues,function(value, i){
data[i]["value"] = value;
})
$scope.data = data;
}, function(response) {
$scope.showError = true;
}).finally(function() {
});
};

Duplication in ng-repeat with filter applied

I am trying to output an object with specific blood group. I am using a Filter for it.
I am getting duplication error on this. I used chrome debugger and found out that its executing multiple times. Data is passed to filter in each iteration and filter do its complete work from scratch each time.
This is my Filter method
Filters.filter('bloodFilter', function() {
var i, filtered = [];
return function(data, bloodGroup) {
if (!data) {
data = GitHub.USERS;
}
if (!bloodGroup) {
bloodGroup = 'O+'
}
for (i = 0; i < data.length; i += 1) {
var value = data[i];
if (value.blood === bloodGroup) {
filtered.push(value);
}
}
return filtered;
}
});
This is My Data
GitHub.USERS = [
{
fname: "Usman",
lname: "Tahir",
blood: "O+"
},
{
fname: "Ali",
lname: "Hassan",
blood: "B+"
},
{
fname: "Aqib",
lname: "Javed",
blood: "AB+"
}
];
This is my Controller
Controllers.controller('BodyController', function($scope) {
$scope.users = GitHub.USERS;
});
and this is my index code
<body ng-controller="BodyController">
<ul>
<li ng-repeat="item in users | bloodFilter">
<b>{{item.fname}}</b> {{item.lname}} {{item.blood }}
</li>
</ul>
</body>
Issue is related to JavaScript Closures. I defined array to return outside return block which created problem.
My Filter Function Should be like this, i and filtered should be inside return block.
Filters.filter('bloodFilter', function() {
return function(data, bloodGroup) {
var i, filtered = [];
if (!data) {
data = GitHub.USERS;
}
if (!bloodGroup) {
bloodGroup = 'O+'
}
for (i = 0; i < data.length; i += 1) {
var value = data[i];
if (value.blood === bloodGroup) {
filtered.push(value);
}
}
return filtered;
}
});

KnockOutJS trigger parent function on child subscribe

I am currently trying to learn KnockOutJS. I thought it would be a great idea to create a simple task-list application.
I do not want to write a long text here, let's dive into my problem. I appreciate all kind of help - I am new to KnockOutJS tho!
The tasks are declared as followed:
var Task = function (data) {
var self = this;
self.name = ko.observable(data.name);
self.status = ko.observable(data.status);
self.priority = ko.observable(data.priority);
}
And the view model looks like this
var TaskListViewModel = function() {
var self = this;
self.currentTask = ko.observable();
self.currentTask(new Task({ name: "", status: false, priority: new Priority({ name: "", value: 0 }) }));
self.tasksArr = ko.observableArray();
self.tasks = ko.computed(function () {
return self.tasksArr.slice().sort(self.sortTasks);
}, self);
self.sortTasks = function (l, r) {
if (l.status() != r.status()) {
if (l.status()) return 1;
else return -1;
}
return (l.priority().value > r.priority().value) ? 1 : -1;
};
self.priorities = [
new Priority({ name: "Low", value: 3 }),
new Priority({ name: "Medium", value: 2 }),
new Priority({ name: "High", value: 1 })
];
// Adds a task to the list
// also saves updated task list to localstorage
self.addTask = function () {
self.tasksArr.push(new Task({ name: self.currentTask().name(), status: false, priority: self.currentTask().priority() }));
self.localStorageSave();
self.currentTask().name("");
};
// Removes a task to a list
// also saves updated task list to localstorage
self.removeTask = function (task) {
self.tasksArr.remove(task);
self.localStorageSave();
};
// Simple test function to check if event is fired.
self.testFunction = function (task) {
console.log("Test function called");
};
// Saves all tasks to localStorage
self.localStorageSave = function () {
localStorage.setItem("romaTasks", ko.toJSON(self.tasksArr));
};
// loads saved data from localstorage and parses them correctly.
self.localStorageLoad = function () {
var parsed = JSON.parse(localStorage.getItem("romaTasks"));
if (parsed != null) {
var tTask = null;
for (var i = 0; i < parsed.length; i++) {
tTask = new Task({
name: parsed[i].name,
status: parsed[i].status,
priority: new Priority({
name: parsed[i].priority.name,
value: parsed[i].priority.value
})
});
self.tasksArr.push(tTask);
}
}
};
self.localStorageLoad();
}
What I want to do in my html is pretty simple.
All tasks I have added are saved to localStorage. The save function is, as you can see, called each time an element has been added & removed. But I also want to save as soon as the status of each task has been changed, but it is not possible to use subscribe here, such as
self.status.subscribe(function() {});
because I cannot access self.tasksArr from the Task class.
Any idea? Is it possible to make the self.tasksArr public somehow?
Thanks in advance!
Try this:
self.addTask = function () {
var myTask = new Task({ name: self.currentTask().name(), status: false, priority: self.currentTask().priority() })
myTask.status.subscribe(function (newValue) {
self.localStorageSave();
});
self.tasksArr.push(myTask);
self.localStorageSave();
self.currentTask().name("");
};

Avoid page flickering when updating a YUI DataTable

I am using jlinq a library for extending linq to json and hence i filter my json data. Consider i have a json data that draws a yui datatable on page load with 100 rows. I am doing a clientside filter which will reduce my json data and i am now redrawing the same datatable. What happens is it works pretty well but with an annoying flickering effect...
I call the below method from onkeyup event of the filter textbox,
function showusersfilter(txt) {
var jsondata = document.getElementById("ctl00_ContentPlaceHolder1_HfJsonString").value;
jsondata = jQuery.parseJSON(jsondata);
var results = jLinq.from(jsondata.Table)
.startsWith("name", txt)
.select();
var jsfilter = { "Table": results };
var myColumnDefs = [
{ key: "userid", label: "UserId", hidden: true },
{ key: "name", label: "Name", sortable: true, sortOptions: { defaultDir: YAHOO.widget.DataTable.CLASS_DESC} },
{ key: "designation", label: "Designation" },
{ key: "phone", label: "Phone" },
{ key: "email", label: "Email" },
{ key: "role", label: "Role", sortable: true, sortOptions: { defaultDir: YAHOO.widget.DataTable.CLASS_DESC} },
{ key: "empId", label: "EmpId" },
{ key: "reportingto", label: "Reporting To", sortable: true, sortOptions: { defaultDir: YAHOO.widget.DataTable.CLASS_DESC} },
{ key: "checkbox", label: "", formatter: "checkbox", width: 20 }
];
var jsonObj = jsfilter;
var target = "datatable";
var hfId = "ctl00_ContentPlaceHolder1_HfId";
generateDatatable(target, jsonObj, myColumnDefs, hfId);
}
My textbox looks
<asp:TextBox ID="TxtUserName" runat="server" CssClass="text_box_height_14_width_150" onkeyup="showusersfilter(this.value);"></asp:TextBox>
and my generatedatatable function,
function generateDatatable(target, jsonObj, myColumnDefs, hfId) {
var root;
for (key in jsonObj) {
root = key; break;
}
var rootId = "id";
if (jsonObj[root].length > 0) {
for (key in jsonObj[root][0]) {
rootId = key; break;
}
}
YAHOO.example.DynamicData = function() {
var myPaginator = new YAHOO.widget.Paginator({
rowsPerPage: 25,
template: YAHOO.widget.Paginator.TEMPLATE_ROWS_PER_PAGE,
rowsPerPageOptions: [10, 25, 50, 100],
pageLinks: 10
});
// DataSource instance
var myDataSource = new YAHOO.util.DataSource(jsonObj);
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
myDataSource.responseSchema = { resultsList: root, fields: new Array() };
myDataSource.responseSchema.fields[0] = rootId;
for (var i = 0; i < myColumnDefs.length; i++) {
myDataSource.responseSchema.fields[i + 1] = myColumnDefs[i].key;
}
// DataTable configuration
var myConfigs = {
sortedBy: { key: myDataSource.responseSchema.fields[1], dir: YAHOO.widget.DataTable.CLASS_ASC }, // Sets UI initial sort arrow
paginator: myPaginator
};
// DataTable instance
var myDataTable = new YAHOO.widget.DataTable(target, myColumnDefs, myDataSource, myConfigs);
myDataTable.resizeHack = function()
{ this.getTbodyEl().parentNode.style.width = "100%"; }
myDataTable.subscribe("rowMouseoverEvent", myDataTable.onEventHighlightRow);
myDataTable.subscribe("rowMouseoutEvent", myDataTable.onEventUnhighlightRow);
myDataTable.subscribe("rowClickEvent", myDataTable.onEventSelectRow);
myDataTable.subscribe("checkboxClickEvent", function(oArgs) {
var hidObj = document.getElementById(hfId);
var elCheckbox = oArgs.target;
var oRecord = this.getRecord(elCheckbox);
var id = oRecord.getData(rootId);
if (elCheckbox.checked) {
if (hidObj.value == "") {
hidObj.value = id;
}
else {
hidObj.value += "," + id;
}
}
else {
hidObj.value = removeIdFromArray("" + hfId, id);
}
});
myPaginator.subscribe("changeRequest", function() {
if (document.getElementById(hfId).value != "") {
if (document.getElementById("ConfirmationPanel").style.display == 'block') {
document.getElementById("ConfirmationPanel").style.display = 'none';
}
document.getElementById(hfId).value = "";
}
return true;
});
myDataTable.handleDataReturnPayload = function(oRequest, oResponse, oPayload) {
oPayload.totalRecords = oResponse.meta.totalRecords;
return oPayload;
}
return {
ds: myDataSource,
dt: myDataTable
};
} ();
}
EDIT:
I even used a delay on the keyup event still the flickering occurs,
var timer;
function chk_me(){
clearTimeout(timer);
timer = setTimeout(function validate(){ showusersfilter(document.getElementById("ctl00_ContentPlaceHolder1_TxtUserName").value);}, 1000);
}
Why do you create a new dataTable each time you filter your data ? You do not need this task. Just supply The filtered data to its dataTable by using sendRequest method of its dataSource
I have create this jsonObject To simulate filtered data
var jsonObject = {
"root":[
{id:"5", userid:"1", name:"ar", designation:"1programmer", phone:"15484-8547", email:"1arthurseveral#yahoo.com.br", role:"1developer", empId:"1789", reportingto:"116"},
{id:"5", userid:"2", name:"br", designation:"2programmer", phone:"25484-8547", email:"2arthurseveral#yahoo.com.br", role:"2developer", empId:"2789", reportingto:"216"},
{id:"5", userid:"3", name:"cr", designation:"3programmer", phone:"35484-8547", email:"3arthurseveral#yahoo.com.br", role:"3developer", empId:"3789", reportingto:"316"},
{id:"5", userid:"4", name:"dr", designation:"4programmer", phone:"45484-8547", email:"4arthurseveral#yahoo.com.br", role:"4developer", empId:"4789", reportingto:"416"},
{id:"5", userid:"5", name:"er", designation:"5programmer", phone:"55484-8547", email:"5arthurseveral#yahoo.com.br", role:"5developer", empId:"5789", reportingto:"516"}
],
"another":[
{id:"5", userid:"5", name:"er", designation:"5programmer", phone:"55484-8547", email:"5arthurseveral#yahoo.com.br", role:"5developer", empId:"5789", reportingto:"516"},
{id:"5", userid:"4", name:"dr", designation:"4programmer", phone:"45484-8547", email:"4arthurseveral#yahoo.com.br", role:"4developer", empId:"4789", reportingto:"416"},
{id:"5", userid:"3", name:"cr", designation:"3programmer", phone:"35484-8547", email:"3arthurseveral#yahoo.com.br", role:"3developer", empId:"3789", reportingto:"316"},
{id:"5", userid:"2", name:"br", designation:"2programmer", phone:"25484-8547", email:"2arthurseveral#yahoo.com.br", role:"2developer", empId:"2789", reportingto:"216"},
{id:"5", userid:"1", name:"ar", designation:"1programmer", phone:"15484-8547", email:"1arthurseveral#yahoo.com.br", role:"1developer", empId:"1789", reportingto:"116"}
]
};
When initializing
(function() {
var Yutil = YAHOO.util,
Ywidget = YAHOO.widget
DataTable = Ywidget.DataTable,
Paginator = Ywidget.Paginator,
DataSource = Yutil.DataSource;
YAHOO.namespace("_3657287"); // QUESTION ID - SEE URL
var _3657287 = YAHOO._3657287;
/**
* paginator
*/
var paginator = new Paginator({
rowsPerPage:25,
template:Paginator.TEMPLATE_ROWS_PER_PAGE,
rowsPerPageOptions:[10, 25, 50, 100],
pageLinks:10
});
/**
* dataSource
*
* As you have static data, I pass an empty "jsonObject" to its constructor
*/
var dataSource = new DataSource({root:[]});
dataSource.responseType = DataSource.TYPE_JSON;
dataSource.responseSchema = {resultsList:"root", fields:[]};
var columnSettings = [
{key:"userid", label:"UserId"},
{key:"name", label:"Name"},
{key:"designation", label:"Designation"},
{key:"phone", label:"Phone"},
{key:"email", label:"Email"},
{key:"role", label:"Role"},
{key:"empId", label:"EmpId"},
{key:"reportingto", label:"Reporting To"}
];
dataSource.responseSchema.fields[0] = "id";
for (var i = 0; i < columnSettings.length; i++) {
dataSource.responseSchema.fields[i + 1] = columnSettings[i].key;
}
/**
* Notice initialLoad equal To false (I suppose your dataTable IS NOT pre-populated)
*/
var dataTableSettings = {
paginator:paginator,
initialLoad:false
};
/**
* dataTable
*
* Notice IT IS STORED in the namespace YAHOO._3657287
*/
_3657287.dataTable = new DataTable("container", columnSettings, dataSource, dataTableSettings);
})();
Now when you want to filter your data, do as follows (Notice sendRequest method)
var i = 0;
YAHOO.util.Event.addListener("reload", "keyup", function(e) {
YAHOO._3657287.dataTable.getDataSource().sendRequest(null, {
success:function(request, response, payload) {
/**
* initializeTable method clear any data stored by The datatable
*/
this.initializeTable();
if(i === 0) {
this.getRecordSet().setRecords(jsonObject["root"], 0);
i++;
} else {
this.getRecordSet().setRecords(jsonObject["another"], 0);
i--;
}
this.render();
},
scope:YAHOO._3657287.dataTable,
argument:null
});
});
You can see here. It works fine!
But if the effect appears again (Notice i am just using relevant part - Nor special feature Nor something else) can occurs because
keyup Event
dataTable rendering
You can set up a variable as follows
var isProcessing = false;
YAHOO.util.Event.addListener("reload", "keyup", function(e) {
if(isProcessing) {
return;
}
isProcessing = true;
YAHOO._3657287.dataTable.getDataSource().sendRequest(null, {
success:function(request, response, payload) {
// as shown above
isProcessing = false;
}
});
}
See also here and here
The problem could be related to the line:
myDataTable.resizeHack = function()
{ this.getTbodyEl().parentNode.style.width = "100%"; }
Since you are resizing the width of the table, it is reasonable to assume that the table will need to be re-painted on the screen resulting in the flicker.

Categories

Resources