How to use KnockoutJS to observe data on server via AJAX? - javascript

I've played a little with knockoutjs and have produced the following example, enough to become excited by the idea of building these viewmodels in javascript so that the view can be written in much simpler, declarative manner, i.e. first you define what you want to observe, then with the data-bind attributes define what you want to happen when your viewmodel changes in certain ways.
But all of this is happening only on the client.
How could I extend this example to use knockoutjs to observe the state of objects on the server e.g. via AJAX calls?
index.htm:
<!doctype html>
<html>
<title>Knockout example</title>
<head>
<script type="text/javascript" src="js/knockout-1.1.1.debug.js"></script>
<script type="text/javascript" src="js/main.js"></script>
<link rel="stylesheet" href="css/main.css" type="text/css"/>
</head>
<body>
<!-- FIRST AREA -->
<div class="infobox">
<div data-bind="visible: noNamesFilled">
<p>This is an example with NO names filled.</p>
</div>
<div data-bind="visible: bothNamesFilled">
<p>This is an example with both names filled.</p>
</div>
<div data-bind="visible: firstNameOnlyFilled">
<p>This is an example with only the first name filled.</p>
</div>
<div data-bind="visible: lastNameOnlyFilled">
<p>This is an example with the last name filled but not the first name</p>
</div>
</div>
<!-- SECOND AREA -->
<p>First name: <input data-bind="value: firstName, valueUpdate:'afterkeydown'" /></p>
<p>Last name: <input data-bind="value: lastName, valueUpdate:'afterkeydown'" /></p>
<div data-bind="visible: bothNamesFilled">
<h2 class="normal">Hello, <span data-bind="text: fullName"></span>.</h2>
</div>
<div data-bind="visible: firstNameOnlyFilled">
<h2 class="informal">Hi there <span data-bind="text: fullName"></span>!</h2>
</div>
<div data-bind="visible: lastNameOnlyFilled">
<h2 class="formal">Hello, Mr. <span data-bind="text: fullName"></span>.</h2>
</div>
<!-- THIRD AREA -->
<div data-bind="visible: noNamesFilled">
<p><span class="bad">:-(</span> Please fill in both names.</p>
</div>
<div data-bind="visible: bothNamesFilled">
<p><span class="good">:-)</span> Good job, both names are filled!</p>
</div>
<div data-bind="visible: firstNameOnlyFilled">
<p><span class="ok">:-(</span> Please fill in the last name, too.</p>
</div>
<div data-bind="visible: lastNameOnlyFilled">
<p><span class="ko">:-(</span> Please fill in the first name as well.</p>
</div>
</body>
</html>
main.css:
* { margin: 0; padding: 0}
body { margin: 10px}
p { margin: 10px}
.infobox {
background-color: #eee;
width: 300px;
height: 100px;
padding: 10px;
}
.informal {
color: purple;
font-family: arial;
}
.normal {
color: black;
font-family: new courier;
}
.formal {
color: black;
font-size: 11pt;
font-family: times roman;
background-color: #eee;
}
.good {
width: 20px;
background-color: lightgreen;
}
.ok {
width: 20px;
background-color: yellow;
}
.bad {
width: 20px;
background-color: tomato;
}
main.js:
window.onload= function() {
var viewModel = {
firstName : ko.observable(''),
lastName : ko.observable('')
};
viewModel.fullName = ko.dependentObservable(function () {
return viewModel.firstName() + " " + viewModel.lastName();
});
viewModel.bothNamesFilled = ko.dependentObservable(function () {
return viewModel.firstName().length > 0 && viewModel.lastName().length > 0;
}, this);
viewModel.firstNameOnlyFilled = ko.dependentObservable(function () {
return viewModel.firstName().length > 0 && viewModel.lastName().length == 0;
}, this);
viewModel.lastNameOnlyFilled = ko.dependentObservable(function () {
return viewModel.firstName().length == 0 && viewModel.lastName().length > 0;
}, this);
viewModel.noNamesFilled = ko.dependentObservable(function () {
return viewModel.firstName().length == 0 && viewModel.lastName().length == 0;
}, this);
ko.applyBindings(viewModel);
}

I would use setTimeout to call a function that uses JQuery to make an $.ajax call. When it returns JSON data, set that data as your view model and finally, setTimeout again to call the function.

Here's an updated example that mainly updates main.js to work with JQuery to do the ajax call.
The HTML file includes Knockout 3 instead of 1. The HTML also includes JQuery latest to make the JQuery functionality work.
The js/server_data.js is there so you have some valid json data to start with. You can change the url in the $.ajax settings to any serverside script you have but try to set its content type to application/json. For example, PHP scripts could set the Content-type header like: header('Content-type: application/json'); before printing out the data in JSON format.
new main.html:
<!doctype html>
<html>
<title>Knockout example</title>
<head>
<script type="text/javascript" src="js/knockout-3.0.0.debug.js"></script>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" src="js/main.js"></script>
<link rel="stylesheet" href="css/main.css" type="text/css"/>
</head>
<body>
<!-- FIRST AREA -->
<div class="infobox">
<div data-bind="visible: noNamesFilled">
<p>This is an example with NO names filled.</p>
</div>
<div data-bind="visible: bothNamesFilled">
<p>This is an example with both names filled.</p>
</div>
<div data-bind="visible: firstNameOnlyFilled">
<p>This is an example with only the first name filled.</p>
</div>
<div data-bind="visible: lastNameOnlyFilled">
<p>This is an example with the last name filled but not the first name</p>
</div>
</div>
<!-- SECOND AREA -->
<p>First name: <input data-bind="value: firstName, valueUpdate:'afterkeydown'" /></p>
<p>Last name: <input data-bind="value: lastName, valueUpdate:'afterkeydown'" /></p>
<div data-bind="visible: bothNamesFilled">
<h2 class="normal">Hello, <span data-bind="text: fullName"></span>.</h2>
</div>
<div data-bind="visible: firstNameOnlyFilled">
<h2 class="informal">Hi there <span data-bind="text: fullName"></span>!</h2>
</div>
<div data-bind="visible: lastNameOnlyFilled">
<h2 class="formal">Hello, Mr. <span data-bind="text: fullName"></span>.</h2>
</div>
<!-- THIRD AREA -->
<div data-bind="visible: noNamesFilled">
<p><span class="bad">:-(</span> Please fill in both names.</p>
</div>
<div data-bind="visible: bothNamesFilled">
<p><span class="good">:-)</span> Good job, both names are filled!</p>
</div>
<div data-bind="visible: firstNameOnlyFilled">
<p><span class="ok">:-(</span> Please fill in the last name, too.</p>
</div>
<div data-bind="visible: lastNameOnlyFilled">
<p><span class="ko">:-(</span> Please fill in the first name as well.</p>
</div>
</body>
</html>
js/main.js:
$(document).ready( function() {
var viewModel = {
firstName : ko.observable(''),
lastName : ko.observable('')
};
viewModel.fullName = ko.dependentObservable(function () {
return viewModel.firstName() + " " + viewModel.lastName();
});
viewModel.bothNamesFilled = ko.dependentObservable(function () {
return viewModel.firstName().length > 0 && viewModel.lastName().length > 0;
}, this);
viewModel.firstNameOnlyFilled = ko.dependentObservable(function () {
return viewModel.firstName().length > 0 && viewModel.lastName().length == 0;
}, this);
viewModel.lastNameOnlyFilled = ko.dependentObservable(function () {
return viewModel.firstName().length == 0 && viewModel.lastName().length > 0;
}, this);
viewModel.noNamesFilled = ko.dependentObservable(function () {
return viewModel.firstName().length == 0 && viewModel.lastName().length == 0;
}, this);
ko.applyBindings(viewModel);
// send request to the server to download the server's model information.
$.ajax(
{
'url': 'js/server_data.js',
'dataType': 'json',
'method': 'post',
'error': function(jqXHR, textStatus, errorThrown)
{
// error callback in case you play with this code and run into trouble.
alert('There was a problem handling the ajax request. The error information is: jqXHR: '
+jqXHR+", textStatus: "+textStatus+", errorThrown: "+errorThrown);
},
'success': function(data)
{
// when it is downloaded and parsed to create the "data" parameter, update the viewModel.
viewModel.firstName(data.firstName);
viewModel.lastName(data.lastName);
}
}
);
}
);
js/server_data.js representing dynamically generated data that might be from a database:
{
"firstName": "John",
"lastName": "Doe"
}
jsteve has the right general idea but don't use setTimeout if you just want to download the data when the page loads. Instead, use the JQuery's document ready callback and JQuery's ajax success callback so things run precisely when you want them to.
If you want to continually listen and react to changes to data in the server, look into long poller techniques. Long poller is more efficient and precisely timed than busy waiting that requires a frequent new request to the server.

Related

Span value inside a style value

this maybe a very stupid question, but is this possibe ?
well i have a sort of a slider on a html page.
this is what it shows up like now:
<p>Illustrator</p>
<div class="w3-light-grey w3-round-xlarge w3-small">
<div class="w3-container w3-center w3-round-xlarge w3-teal" style="width:75%">75%</div>
</div>
This shows up a bar,
well what want to achieve is if its possible to change that value 75% to my script data :
style="width:75%">
like i have a script, it retrieves values from my server:
var input = "10;11;15";
var arr = input.split(";");
document.getElementById("humid").innerHTML = (arr[0 ]);
this shows up my data just normal
<span id="humid">0</span>
what i want to do is something like this, but i don't know how:
I want this value from style="width:75%"> to be the humid value.
so if my humid value is 50% the width goes 50%
i did try this but no result
style="width:humid+%">
or style="width:(humid)+%">
i'm still learning,
regards
<!DOCTYPE html>
<html>
<title>TESt</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto'>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<style>
html,body,h1,h2,h3,h4,h5,h6 {font-family: "Roboto", sans-serif}
</style>
<body class="w3-light-grey">
<p>Original</p>
<div class="w3-light-grey w3-round-xlarge w3-small">
<div class="w3-container w3-center w3-round-xlarge w3-teal" style="width:75%">75%</div>
</div>
<p>Media</p>
<div class="w3-light-grey w3-round-xlarge w3-small">
<div class="w3-container w3-center w3-round-xlarge w3-teal" style="#humid">0</div>
</body>
<script>
var input = "10;11;15";
var arr = input.split(";");
//alert(arr[1 ]);
document.getElementById("humid").innerHTML = (arr[0]);
document.getElementById("temp").innerHTML = (arr[1 ]);
document.getElementById("uv").innerHTML = (arr[2 ]);
</script>
</html>
Retrieve my input:
function readForestall() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("ForestAll").innerHTML =
this.responseText;
}
};
xhttp.open("GET", "readFORESTALL", false);
xhttp.send();
}
setInterval(function() {
readForestall();
}, 5000);
If I understand your question right, you want to change the width of an element based on a variable you receive. In that case you can change the inline style with JavaScript. You just need to grab the element and set the style by assigning values to the properties of the element's style property.
If you want to change the width, you can use the following:
element.style.width = '50%'
It sets the width to 50%. You can also include a variable like this:
const width = 50
element.style.width = `${width}%`
I've created a snippet below where you can set the value by using an input and it updates both the width and the content of that div. You can click on the "Run code snippet button" and see the result in live.
const barLeft = document.querySelector('#bar-left');
const barRight = document.querySelector('#bar-right');
const input = document.querySelector('[name="width"]');
const error = document.querySelector('.error');
input.addEventListener('input', (e) => {
const widthLeft = Number(e.target.value);
const widthRight = 100 - widthLeft;
if (widthLeft < 0 || widthLeft > 100) {
error.textContent =
"We don't do that here. Width must be between 0 and 100.";
return;
}
error.textContent = '';
barLeft.textContent = `${widthLeft}%`;
barLeft.style.width = `${widthLeft}%`;
barRight.textContent = `${widthRight}%`;
barRight.style.width = `${widthRight}%`;
});
body {
font-family: sans-serif;
margin: 0;
text-align: center;
}
.outer {
display: flex;
margin-bottom: 2rem;
}
.inner {
background-color: lightcoral;
box-shadow: 0 -4px 0 rgba(0, 0, 0, 0.2) inset;
height: 100%;
padding: 1rem;
}
.inner--blue {
background-color: lightblue;
}
.controls {
margin-bottom: 1rem;
}
input {
font-family: inherit;
font-size: inherit;
padding: 0.5rem 1rem;
}
.error {
color: #d32f2f;
}
<div class="outer">
<div class="inner" id="bar-left" style="width: 75%">75%</div>
<div class="inner inner--blue" id="bar-right" style="width: 25%">25%</div>
</div>
<div class="controls">
<label for="width">First bar's width:</label>
<input type="number" name="width" id="width" min="5" max="95" value="75" />
</div>
<div class="error"></div>
Update: Updated your example below. Make sure you close your tags, and check out how to add ids to elements.
<body class="w3-light-grey">
<p>Media</p>
<div class="w3-light-grey w3-round-xlarge w3-small">
<div class="w3-container w3-center w3-round-xlarge w3-teal" id="humid">
0
</div>
</div>
div>
<script>
var input = '10;11;15';
var arr = input.split(';');
// update the content of the div with ID "humid"
document.getElementById('humid').textContent = arr[0];
// change the width of the div with ID "humid"
document.getElementById('humid').style.width = `${arr[0]}%`;
</script>
</body>
Tranq,
i am not exactly sure why you would want to call the width from an array and use DOM when you can simply use CSS to accomplish this with #media instead. correct me if i am wrong, but you're just trying to adjust the width based on values of the current width of the device? this seems like a whole lot of work for what is a simple solution.
use something like to set the appropriate widths depending on the screen widths:
#media only screen and (max-width: 600px) {
#humid { width: 75%; }
}
also, it is likely you're not passing your array properly and it is returning NULL(0). you can check this via a debugger and ensure it is being passed properly. FireFox has a Great built in debugger for this. use CMD/CTRL+SHIFT+K to open the debugger in FireFox.
P.S. you are not passing the array properly. you're setting it to change the 'innerHTML' which changes everything inside of the
document.getElementById("humid").innerHTML = (arr[0 ]);
if you change "(arr[0 ]);" to something like "string" it will replace the value 0 to "string".

which element contains #id idString return index

Multiple elements with "column" class.
Find which column element contains a id: idString, return index of the !column! that contains the id
This is the new (3rd) html I am searching:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Style-Type" content="text/css">
<meta name="generator" content="Amazon.com">
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css" class="dontsplit"> .column *{ padding: 20px; }
</style>
</head>
<body class="calibre" style="margin: 0px; padding: 0px; width: 101024px;">
<div class="calibre67">
<div class="last first column" style="width: 616px; float: left;">
<p class="calibre2" style="padding-top: 8px; padding-bottom: 8px;">
<span class="calibre3">bla , bla etc.</span>
</p>
</div>
<div class="column" style="width: 616px; float: left;">
<div class="calibre1 split">
<p class="calibre2" style="padding-top: 8px; padding-bottom: 8px;">
<span class="calibre3">bla bla etc.</span>
</p>
</div>
</div>
<!-- div contains the ID I want to find, structure of page varies with different files -->
<div class="column" style="width: 616px; float: left;">
<div class="calibre1 split">
</p>
<p class="calibre4" style="padding-top: 8px; padding-bottom: 8px;">
<!-- This is the id I want to find, so the function should return 2 (3rd column). -->
<span id="1P-09761e033cf14df8aeccf069ebe7886e" class="calibre6">Prologue:</span>
</p>
<p class="calibre5" style="padding-top: 8px; padding-bottom: 8px;">
<span class="calibre6">It had been bla, bla etc.</span>
</p>
</div>
</div>
</div>
</body>
</html>
My Code:
var idString = "O-09761e033cf14df8aeccf069ebe7886e";
function findID(idString) {
$(".column").each(function(index, element){
if ($(this).find('*').attr('id') == idString) {
console.log(index);
return index;
}
});
return;
}
var result = findID(idString);
// should return/log 2
// currently returns/logs nothing
Function should return 2 in this example.
I have tried very many combinations of your suggestions.
What am I doing wrong?
if ($idString) will always return true. Instead of that check, you'll want to check against the .attr() of each element with if ($(this).attr('id') == idString).
Note that the attribute doesn't actually contain the #, so you'll want to remove the # from the ID variable, or make use of .contains().
This can be seen in the following:
var idString = "O-09761e033cf14df8aeccf069ebe7886e";
function findID(idString) {
$(".column").each(function(index, element) {
if ($(this).attr('id') == idString) {
console.log(index);
return index;
}
});
return;
}
findID(idString);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p class="column">“When the grav emitters are charged"</p>
<p class="column">“It wasn’t that bad this time”</p>
<p class="column" id="O-09761e033cf14df8aeccf069ebe7886e">Chapter 1</p>
<p class="column">“I can monitor engineering from up here”</p>
Also note that the index is zero-based.
You could use the jQuery .index(selector) method. It will return the index of the matched element (on the left hand side) within the collection of the selector on the right hand side.
A selector representing a jQuery collection in which to look for an element.
var idString = "#O-09761e033cf14df8aeccf069ebe7886e";
function findID(idToSearch) {
return $(idToSearch).index(".column p");
}
var result = findID(idString);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="column">
<p>“When the grav emitters are charged"</p>
</div>
<div class="column">
<p>“When the grav emitters are charged"</p>
</div>
<div class="column">
<p id="O-09761e033cf14df8aeccf069ebe7886e">Chapter 1</p>
<p>“I can monitor engineering from up here”</p>
</div>
<div class="column">
<p>“When the grav emitters are charged"</p>
</div>

My WebApp where withSuccessHandler returns undefined

I need help sorting this out. Everything seems to work fine until it gets to receive the results of request, where someting goes wrong. The idea of the project is a Web App, where user gets 2 fields for name and date of birth. After filling them the information is verified by checking in the spreadsheet with relevant information. If everything is okay, the program next obtains appropriate id with getSheetId(), generates <iframe> code for access and should be returning it with HtmlService. However, something makes it return undefined.
Code.gs
function doGet() {
Logger.log("Login is loading")
return HtmlService
.createTemplateFromFile('index')
.evaluate()
//.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function evaluate(name, date) {
var t = SpreadsheetApp.openById('1nQxfaQcNdM6S1roJs6gxTaZPtM5gOublch5jVKDhkho')
.getSheetByName('key');
Logger.log(t.getSheetId());
Logger.log(name);
Logger.log(date);
var v = t.getRange("B2:D200");
var i = 0;
for (i = 1; i < 200; i++) {
if (v.getCell(i, 2).getValue() == name) {
Logger.log(v.getCell(i, 2).getValue());
Logger.log(name & " = the name");
Logger.log(new Date(v.getCell(i,3).getValue()).getTime());
var pdate = date.split(".");
Logger.log(new Date(pdate[2],pdate[1],pdate[0]).getTime());
var t = v.getCell(i,3).getValue();
Logger.log(pdate);
Logger.log(t.getFullYear());
Logger.log(t.getMonth());
Logger.log(t.getDate());
if (new Date(t.getFullYear(),t.getMonth()+1,t.getDate()).getTime() == new Date(pdate[2],pdate[1],pdate[0]).getTime()) {
Logger.log("match found");
include(v.getCell(i,1).getValue());
break;
} else {
Logger.log("Bad date");
return "<h2>Wrong Input. 404.</h2>";}
}
}
if (i >= 199) {
Logger.log("Bad name");
return "<h2>Wrong Input. 404.</h2>";
}
}
var hhtml = "";
function include(name) {
var html = '<iframe src="https://docs.google.com/spreadsheets/d/e/2PACX-1vTeFjXOSsBRAKjbFLUSBGZOXtmjZO_4RtxxrQtXbk9sxZkF5Kdjs9OIs0tSQwekjYbOTn7JJ-_iCdeD/pubhtml?gid='
var t = SpreadsheetApp.openById('1nQxfaQcNdM6S1roJs6gxTaZPtM5gOublch5jVKDhkho').getSheetByName(name);
if (t == null) {
Logger.log("Page not found");
return "<h1>Wrong Input. 404.</h1>"
} else {
html += t.getSheetId();
}
Logger.log(name);
html += '&single=true&widget=true&headers=false" width="80%" height="600"></iframe>'
html += '<br><br><br><h3>Рейтинг групи</h3><iframe src="https://docs.google.com/spreadsheets/d/e/2PACX-1vRF1MobEoKdxvO_SopGTvl-WzqEQ3nQXd6Jo_a7RTAg09yluO32AClwd4krWnVHXGQllPPwOsDeYYzN/pubhtml?gid=2096708929&single=true&widget=true&headers=false" width="80%" height="600"></iframe>'
Logger.log(html);
hhtml = HtmlService.createHtmlOutput(html).getContent();
return HtmlService.createHtmlOutput(html).getContent();
}
function getHtmlCode() {
return hhtml;
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<div id="mainForm">
<h2> Будь-ласка введіть своє прізвище українською мовою та дату народження у форматі день.місяць.рік (наприклад: 22.06.2018) </h2>
<form>
<div>
<div class="inline form-group">
<label for="name">Прізвище</label>
<input type="text" id="nameInput" style="width: 150px;">
</div>
<div class="inline form-group">
<label for="date">Дата Народження</label>
<input type="text" id="dateInput" style="width: 150px;">
</div>
</div>
<button class="action" type="button" id="submitButton">Submit</button>
</form>
</div>
<style>
.hidden {
display:none;
}
.form-group {
margin: 2px 0px;
}
#submitButton {
margin: 4px 0px;
}
body {
margin-left: 50px;
}
</style>
<script>
$(function(){
console.log('startup')
$('#submitButton').on('click', function(){
console.log("data get");
function respondent(value)
{
$('#mainForm').toggleClass('hidden');
console.log("script is success");
console.log(value);
document.getElementById('Response').innerHTML = value
}
google.script.run.withSuccessHandler(respondent).evaluate(document.getElementById('nameInput').value, document.getElementById('dateInput').value);
})
})
</script>
<div id="Response">
<h2>Waiting for data...</h2>
</div>
</body>
</html>
After cleaning up your code formatting, it looks like you're only returning a value from evaluate() during the Wrong Input error conditions. Nothing is returned otherwise.
Perhaps you meant to add a line like this at the end of evaluate():
return include(name);
Also, the hhtml global variable and getHtmlCode() functions aren't useful in the context of Apps Script. Each execution is in a separate instance with no state being carried over. Which mean getHtmlCode() will always return an empty string.

Add players to a list with AngularJS

I'm new at AngularJS. I do know some javascript, but AngularJS seems hard to learn (maybe it's just me).
My problem is as follows...
I have a list of players and I would like to make it possible for a user (coach or whoever) to add their players to the list. I have tried couple of method for these past few days, and I just can't figure it out.
Code is below, and u can check out my plunkr here:
HTML
<!DOCTYPE html>
<html >
<head>
<!-- CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link href="style.css" rel="stylesheet" />
<!-- Scripts -->
<script data-require="angular.js#1.2.17" data-semver="1.2.17"
src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-app="app">
<div ng-controller="MoveCtrl" class = "container">
<div class = "row">
<div class = "col-xs-4 left-space">
<!--Forgot to add this block of code for input-->
<label>Player name: </label> <input #playerName/>
<button (click) = "add(playerName.value); playerName.value = ''">
Add
</button>
<!--Rest is the same-->
<label class="left-space-title" for="aplayers">Available Players</label>
<select class="left-space-container" size="5" multiple ng-model="available"
ng-options="player as player.name for player in availableplayers">
</select>
</div>
<div class = "col-xs-2 mid-space ">
<input id="moveright" type="button" value=">>"
ng-click="moveItem(available, availableplayers,selectedplayers)" />
<input id="moveleft" type="button" value="<<"
ng-click="moveItem(selected, selectedplayers,availableplayers)" />
</div>
<div class = "col-xs-4 right-space">
<label class="right-space-title" for="splayers">Selected Players</label>
<select class="right-space-container" size="5" multiple ng-model="selected"
ng-options="player as player.name for player in selectedplayers">
</select>
</div>
</div>
</div>
</div>
</body>
</html>
CSS
.mid-space {
margin-top: 30px;
}
.left-space__title,
.right-space__title {
width: 100%;
}
.left-space-container,
.right-space-container {
width: 100%;
min-height: 500px;
}
#moveright,
#moveleft {
width: 100%;
}
Javascript
angular.module('app', []).controller('MoveCtrl', function($scope) {
$scope.moveItem = function(items, from, to) {
items.forEach(function(item) {
var idx = from.indexOf(item);
if (idx != -1) {
from.splice(idx, 1);
to.push(item);
}
});
};
$scope.selectedplayers = [];
$scope.availableplayers = [
{
id: 1,
name: 'foo'
},
{
id: 2,
name: 'boo'
},
{
id: 3,
name: 'goo'
}
];
});
Plunkr is here if you would like to fiddle with the code directly
The plunker has been updated with your working code changes to add the new player to the available players list
add new player plunker
$scope.addItem = function(){
var numberOfplayers = $scope.availableplayers.length;
var newPlayer = {
id : numberOfplayers + 1,
name : $scope.newPlayerName
}
console.log($scope.newPlayerName);
$scope.availableplayers.push(newPlayer);
}
<label>Player name: </label> <input ng-model="newPlayerName"/>
<button ng-click = "addItem(playerName);">
Add
</button>
code changes done .
1.set the ng-model for the input field .
2.get the value of the
input fiend in controllers add player method.
3.create the new
players object with the generated id value.
4.Insert the new player
object to the availableplayers scope variable which inturn pops up
the value in the box.

jQuery/Javascript : How to prepend elements without going back to the top of last prepended element?

Whenever I click prepend, after all elements are prepended, the view of the chat area switches to the top of the chat area or the last prepended element. This is different from append, whereby after all elements are appended, the view of the chat area does not switch to the end of the chat area or last appended element but still stays at its previous position.
How do I make the prepend function act in the same way as append in the sense that the view of the chat area does not change similar to FB's load previous message function?
Here is a sample code that illustrates what I mean.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript" src="http://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
</head>
<style type="text/css">
.chatbox{
border:1px solid #2a6496;
height: 600px;
margin-top:50px;
}
.chatbox div{
height: 100%;
}
.rightP{
border-left: 1px solid #2a6496;
}
.rightP .contents{
border-bottom: 1px solid #2a6496;
height: 70%;
}
.rightP .send{
padding : 5% 5% 5% 5%;
height: 30%;
}
#response{
height: 200px;
overflow-y: scroll;
overflow-wrap: break-word;
}
</style>
<script>
function appendMessage()
{
var data = 'hello';
var message = document.createElement('p');
message.innerHTML = data;
console.log(message.innerHTML);
$('#response').append(message);
$('#response').append($('.load'));
}
function prependMessage()
{
for(var $i = 0;$i<10;$i++)
{
var data = 'hello'+$i;
var message = document.createElement('p');
message.innerHTML = data;
console.log(message.innerHTML);
$('#response').prepend(message);
$('#response').prepend($('.load2'));
}
}
</script>
<body>
<div class="container">
<div class="chatbox">
<div class="col-sm-8 rightP">
<div class="row contents">
<div class="row msg">
<div id="response" class="msg form-group">
<a onclick="return appendMessage()" class="load btn btn-default">Append</a>
<a onclick="return prependMessage()" class="load2 btn btn-default">Prepend</a>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
In HTML there should be return in your onlick:
<a onclick="return loadMessage();" class="load btn btn-default">Load More Msg</a>
In JS you need to add return false to your function loadMessage.
var loadMessage = function(){
var firstMessage = $messagesWrapper.find('> p:nth-child(2)');
$.ajax({
....
if(messages.length<10){
$('.load').hide();//hide the load button if remaining messages to load is <10
}
success: function(messages){
$.each(messages, function() {
prependMessage(this);
});
},
....
});
return false;
};
Try this:
<div id="response" class="msg form-group">
<a onclick="loadMessage(); return false;" class="load btn btn-default">Load More Msg</a>
</div>
If this doesn't work, try another way:
var loadMessage = function(){
e.preventDefault(); // preventing any scroll action
var firstMessage = $messagesWrapper.find('> p:nth-child(2)');
(...)
And
var prependMessage = function(data){
e.preventDefault(); // preventing any scroll action
var message = document.createElement('p');
(...)
If this doesn't work please provide the whole code so we can reproduce.

Categories

Resources