Pass an array of checkbox values to a Javascript string - html

What I want to happen with the following code is when a user checks multiple data centers and then selects a type of change it will automatically refresh the description and Impact text area with a unique string statement including the data centers the user has chosen.
Can someone advise me what I am doing wrong?
JS:
function updateDescImpact() {
var changeSel = document.changeform.change_type;
var ChangeType = (changeSel.options[changeSel.selectedIndex].value);
var description = " ";
var impact = " ";
var data_center = "";
var inputs = document.getElementsByTagName('input');
for (var x = 0; x < inputs.length; x++) {
{
if (inputs[x].type == "checkbox" && inputs[x].name == 'data_center[]') {
if (inputs[x].checked == true) {
data_center += inputs[x].value + ',';
}
}
}
if (/,$/.test(data_center)) {
data_center = date_center.replace(/,$/, "")
}
if (ChangeType == "Filer Changes") {
description = "This is the Filer Change Description for $('data_center')";
impact = "This is the Filer Changes impact statement for $('data_center')";
} else if (ChangeType == "DNS Changes") {
description = "This is the DNS Change Description for $('data_center')";
impact = "This is the DNS Changes impact statement for $('data_center')";
} else {
description = "";
impact = "";
}
document.changeform.description.value = description;
document.changeform.impact.value = impact;
}
HTML:
<form action="" id="changeform" method="post" name="changeform">
<input type="submit" value="submit change">
<table>
<tr valign="top">
<td><strong>Data Center</strong></td>
<td><input name="data_center[]" type="checkbox" value="zone1">Zone1
<input name="data_center[]" type="checkbox" value=
"Zone2">Zone2</td>
</tr>
<tr valign="top">
<td><strong>Change Type</strong></td>
<td><select id="change_type" name="change_type" onchange=
"updateDescImpact()">
<option value="DNS Changes">
DNS Changes
</option>
<option value="Filer Changes">
Filer Changes
</option>
</select></td>
</tr>
<tr valign="top">
<td><strong>Description</strong></td>
<td>
<textarea cols="50" id="description" name="description" rows="10">
This text needs to be updated
</textarea>
</td>
</tr>
<tr valign="top">
<td><strong>Service Impact</strong></td>
<td>
<textarea cols="50" id="impact" name="impact" rows="10">
This text needes to be updated
</textarea></td>
</tr>
</table>
</form>

With only a minimal idea of what it is you want to do with the string, once you've got it, I've put together the following:
function updateDescImpact() {
// declaring all the variables, separating them with commas, at once.
// retrieves the element:
var changeSel = document.getElementById('change_type'),
// defines the various default messages:
changes = {
'DNS': {
'impact': 'This is the DNS Change impact statement for $("data_centre")',
'description': 'This is the DNS Change Description for $("data_center")'
},
'Filer': {
'impact': 'This is the Filer Changes impact statement for $("data_centre")',
'description': 'This is the Filer Change Description for $("data_center")'
}
},
ChangeType = (changeSel.options[changeSel.selectedIndex].value),
// creates an array:
data_center = [],
inputs = document.getElementsByTagName('input');
for (var x = 0, len = inputs.length; x < len; x++) {
{
if (inputs[x].type == "checkbox" && inputs[x].name == 'data_center[]') {
if (inputs[x].checked === true) {
// adds the checkbox-values to the array:
data_center.push(inputs[x].value);
}
}
}
// creates another key for the 'changes' object,
// the value of which is determined by whether the array contains values,
// if it *does*, then those values are joined together using the given string,
// if *not*, then the value is an empty string.
// in both cases the value is followed by a period.
changes.data_centers = (data_center.length > 0 ? ': ' + data_center.join(', ') : '') + '.';
document.getElementById('description').value = changes[ChangeType].description + changes.data_centers;
document.getElementById('impact').value = changes[ChangeType].impact + changes.data_centers;
}
}
JS Fiddle demo.
In the next part I'll try to explain the changes I've made, but the simple, central, premise is to minimise the amount of work you're doing, and to reduce the scope for errors to creep in.
Where you were using:
document.changeform.change_type;
I've chosen, instead, to use:
var changeSel = document.getElementById('change_type');
This is because using the id of the relevant element is faster than navigation through the DOM using two ids, especially since there's no guarantee that the element will be recognised by its id when it's used as a global variable without being declared. It works almost everywhere, yes, but it's not a standard, or specified, behaviour in JavaScript and was a convenience offered, initially, in Internet Explorer. This convenience is not guaranteed to present in future, however.
Also, an id is (and must be) unique within the document, therefore there's no sense in using two, when one will do perfectly well.
The use of an object to contain the various alternative strings you (might) want to use, this is to reduce the need for multiple if/else if/else statements. And makes it somewhat easier to update the function in future to address new options and requirements. As stated, above, this is to minimise the amount of work you have to do (now, and in the future).
Where you used:
if (/,$/.test(data_center)) {
data_center = date_center.replace(/,$/, "")
}
I chose, instead, to use an array:
data_center = [],
This allows us to avoid having to use regular expressions which, while in this case was simple enough, is not always the case and, again, is more difficult than necessary for the finished result. Whereas an array can use the .join() method (used later) which will join the array elements together with the defined string, and will not result in a trailing-comma.
I've also used getElementById() in the last two lines to assign the various strings/values to the relevant elements. For precisely the same reason as outlined above.

Related

Allow Single Type Input in Html

Ok so i have a Chatroom, People are required to type in a name and once done they may chat. But alot of times people take other peoples name. So what i need is kind of like a limited input
here is the input:
<input type="text" class="input-block-level" ng-model="name" ng-change="setName()" placeholder="Your Name" maxlength="10" required></div>
I need this so if i type in the name Bob, no one can use that name again
Incase you want the website: https://project-js-imthatguy.c9users.io/
One possible way to check amongst a given set...
used a simple for loop for this example
Inside your send function that you have defined, perform a check:
$scope.send = function send() {
var isDuplicated = 0
for (i = 0; i < $scope.roster.length; i++){
if ($scope.roster[i] == $scope.name){ isDuplicated++ }
}
if (isDuplicated > 1){
//make an error message appear here
//quick and dirty solution
alert('Please choose a name that has not been taken by someone else')
//suggestion set a variable here to true and then use that variable to show/hide a div below the user input area
}
else {
console.log('Sending message:', $scope.text);
socket.emit('message', $scope.text);
$scope.text = '';
}
};

Adding two javascript functions(depending on input)

I have put together a calculator what calculates a price, depending on user input. It works fine with one input, but now I have to scale it a little with a second user input. But here's the catch: the user might not want to put anything to the field, so it will be empty. And that's the thing that brakes my code. I could duplicate the calculator function and return the values and add those two together in a third function, but it will not work when there's an empty value.
Just for the sake of it, some trivial HTML code:
//When I only calculate with this user input, its easy
<input type="text" id="rocktext"><br>
// But how to consider this and do the same exact calculations like with the
//first one and add those two result together?
<input type="text" id="rocktext2"><br>
The code in the end should look like:
Take first user input, calculate the price(like in code below)
IF(!!) there is a second user input, calculate the price and add it to
the first one
Am I being a moron to try it with JS or just a moron in the firstplace?
Hope to hear from You, guys!
J.
The initial JS code is as follows:
function priceCalc() {
var inputs = document.getElementById("rocktext").value;
var length = inputs.length;
var accept = 6;
var initPrice = 8;
if (inputs<=accept){
// Since the code is much simpler right now i just put the result in HTML as follows:
document.getElementById("rockpricetotal").innerHTML = initPrice + " dollars";
//I can also return the the value calculated here like so:
//retVal = initPrice;
}
else {
var intLength = parseInt(length, 10);
var lengthGap = intLength - accept;
var totals = lengthGap * 0.8 + initPrice;
var prec = totals.toPrecision(3);
// Since the code is much simpler right now i just put the result in HTML as follows:
document.getElementById("rockpricetotal").innerHTML = prec + " dollars";
// Here also the return clause can be possible with the calculation result like so:
//retVal = prec;
}
// And the final return as an alternative to the innerHTML :
// return retVal;
}
Making it scalable, you can add a class to all the inputs which may be in the function (something like calcInput), so you iterate all of them and if the value isn't empty (and if it's a valid number), you put it in the calculation.
Or you can just verify if the second input is empty, if so, calls functionOne, if not, calls functionTwo:
function twoDifferentWays() {
var valueOne = document.querySelector("#rocktext").value;
var valueTwo = document.querySelector("#rocktext2").value;
if (!!valueTwo && !isNaN(valueTwo)) {
callsFunctionOne(valueOne, valueTwo);
} else {
callsFunctionTwo(valueOne, valueTwo);
}
}

Search HTML5 Datalist by Value and inner text (or some other property)

I am attempting to find a way so that when a user enters text into the data list, they can come across the same entry by course number (E.G. "CS 101") or course name (E.G. "Intro to Computer Science).
Currently, what I have is only searchable by the value field:
<datalist id="tagList">
<option value=""></option>
<option value="CSCI 4950">Senior Software Project</option>
<option value="CSCI 5117">Developing the Interactive Web</option>
<option value="CSCI 5421">Advanced Algorithms</option>
<option value="CSCI 5980">Design Methods for Comp. Sci.</option>
</datalist>
The solution needs to work in the Android Webkit web browser (Phonegap) -- Chrome seems to handle Datalists the same as Android's native browser so if it works in Chrome I should be ok.
It needs to display both the course name and course number to the user
This needs to be generalizable and not hard-coded as I am using AngularJS to actually populate the full list of courses.
What I've tried
https://stackoverflow.com/a/22827978/2831961 -- For some reason, this didn't work.
I've also tried a similar strategy, but with the data-value attribute. That didn't work either. Unless I am responsible for some behind the scenes Javascript work that I am unaware of.
http://jsfiddle.net/rh48cgrj/3/
Here's a fiddle. I put the option values/text into key:value pairs in a javascript object. NOTE: the key is an index number and the value is the option value attribute AND the text. This makes it easier to search them for our text.
var i = 0;
var selectItems = {}
$('#tagList option').each(function() {
var listvalue = $(this).val();
var listtext = $(this).text();
selectItems[i] = listvalue + " " + listtext + ",";
i++;
});
Then I split them into rows that included both value and text.
count = i;
for(i=0; i < count;i++) {
var blockoftext = blockoftext + " " + selectItems[i].toLowerCase() + ",";
}
I then setup a search function that would search those rows to see if any returned a match, and if they did the result was outputted to a div below the search box.
var texttosplit = blockoftext.split(",");
var searchresults;
for(i=0; i < texttosplit.length; i++) {
(texttosplit[i].indexOf(searchvalue.toLowerCase()) != -1) ?
(searchresults = texttosplit[i] + "<br>") : false;
$("#searched").html(searchresults);
}
There's an example for all of the above in the fiddle.
EDIT: The below is the commented code for the loop to check if search text is in the datalist per op request.
for (i = 0; i < texttosplit.length; i++) {
//The above loops through our array of class values and titles
(texttosplit[i].indexOf(searchvalue.toLowerCase()) != -1) ?
// The above determines if our search text is in class title using a ternary operator
// our array of class values and titles is lowercase so we make
//sure our search text is lowercase as well
// if we find a match between the search text and the class title/values perform the following:
(searchresults = texttosplit[i].replace(/\b[a-z]/g, function(letter) {
return letter.toUpperCase();
})
// The above replaces the first char of every word with an uppercase char
.replace("Csci", "CSCI") + "<br>",
// The above finds Csci and changes it to CSCI since all THOSE letters should be uppercase
prevtext = $("#searched").html(),
//get current text of element with id "searched" and place it in prevtext
$("#searched").html(prevtext + searchresults))
//append "searched" by adding it's current text with the new searchresults
:
//if search text is not in the class title return false
false;
}

How to filter or custom filter array of objects based on matching values from another object

I implemented an advance search with 15 input fields in AngularJS.
In the page load itself the result set is return from database in JSON format and i need to do the filter in client side only.
The input criteria's equivalent column is available in the result set and i need to check in its respective column only.
I am converting each column by JSON.stringify() and check with the search params like the below :
$scope.filteredData = $scope.actualData.filter(function(item) {
return JSON.stringify(item.FirstName).toLowerCase().indexOf(lowerFirstName) != -1 &&
JSON.stringify(item.LastName).toLowerCase().indexOf(lowerLastName) != -1 &&
JSON.stringify(item.EmailAddress).toLowerCase().indexOf(lowerEmailAddress) != -1 &&
JSON.stringify(item.Address1).toLowerCase().indexOf(lowerAddress1) != -1 &&
JSON.stringify(item.Address2).toLowerCase().indexOf(lowerAddress2) != -1;
...... etc // upto 15 fields
});
Since i have the 15 input fields and the actual result set contains a minimum of 50,000 records.
So converting each record's each column by JSON.stringify() and check with search params will surely cause the performance issue.
Is there any other way to achieve the filtering in client side with other approach.
I posted a sample code in Plunker with 5 input fields only : http://plnkr.co/edit/nUWZEbGvz7HG6gb91YZP
sylwester's answer is the normal way you'd filter things. Your code looks like you want to filter down to only the object that matches every input field. You code attempts to find an object where every property matches the searchParams object. At that point, I don't see what benefit there is to finding that object, because the user already created the object again! Nonetheless, here's a proper version of your code:
Live demo here.
<div ng-repeat="data in actualData | filter:searchData()">
$scope.searchData = function() {
return function(item) {
return Object.keys(item).every(function(key) {
// skip the $$hashKey property Angular adds to objects
if (key === '$$hashKey') { return true; }
var searchKey = key.charAt(0).toLowerCase()+key.slice(1);
return item[key].toLowerCase() === $scope.searchParams[searchKey].toLowerCase();
});
};
};
You really need to limit the data coming from the server for the browser's sake and for the server's sake. It's easy to implement a LIMIT, OFFSET system. It sounds like, overall, you just need to be able to query the server for a certain record.
From your comments, it seems you definitely want Angular's built in filter filter:searchParams, and just capitalize your searchParams models to match your data. For fun, I'll include more options for finer tuning.
This one almost mimics filter:searchParams. You can change > 1 to adjust when the partial matching kicks in, or have it return true only when both items are strictly equal === to disable partial matching. The difference here is that all items are hidden until matched, whereas filter:searchParams will show all items and then remove what doesn't match.
Live demo here.
$scope.searchData = function() {
return function(item) {
return Object.keys(item).some(function(key) {
if (key === '$$hashKey') { return false; }
var searchKey = key.charAt(0).toLowerCase()+key.slice(1);
var currentVal = $scope.searchParams[searchKey].toLowerCase();
var match = item[key].toLowerCase().match(currentVal);
return currentVal.length > 1 && match;
});
};
};
Lastly, to perfectly mimic filter:searchParams, you'd just put in a check to NOT filter the items until there is user input and the input is long enough to start the partial match.
Live demo here.
$scope.searchData = function() {
var partialMatchLength = 2;
return function(item) {
var shouldFilter = Object.keys($scope.searchParams).some(function(key) {
return $scope.searchParams[key] && $scope.searchParams[key].length >= partialMatchLength;
});
if (!shouldFilter) { return true; }
return Object.keys(item).some(function(key) {
if (key === '$$hashKey') { return false; }
var searchKey = key.charAt(0).toLowerCase()+key.slice(1);
var currentVal = $scope.searchParams[searchKey].toLowerCase();
var match = item[key].toLowerCase().match(currentVal);
return currentVal.length >= partialMatchLength && match;
});
};
};
First of all you ng-repeter with 50.000 records more likely is going to kill your browser, so you should thing about pagination.
Secondly you can easy filter your data using angular filter please see that demo
http://plnkr.co/edit/R8b8G4xCMSQmX1144UJG?p=preview
<div ng-controller="ListCtrl">
<br />
First Name:
<input type="text" id="txtFirstname" ng-model="searchParams.FirstName">
<br/>Last Name:
<input type="text" id="txtLastname" ng-model="searchParams.LastName">
<br/>Email Address:
<input type="text" id="txtEmailAddress" ng-model="searchParams.EmailAddress">
<br/>Address 1:
<input type="text" id="txtAddress1" ng-model="searchParams.Address1">
<br/>Address 2:
<input type="text" id="txtAddress2" ng-model="searchParams.Address2">
<br/>
<button class="btn btn-primary" ng-click="searchData()">Search</button>
<br />
<hr />
<b>Filtered Data(s):</b>
<div ng-repeat="data in actualData | filter:searchParams ">
<span ng-bind="data.FirstName"></span>
<span ng-bind="data.LastName"></span> |
Address : {{data.Address1}}
</div>
<hr />
</div>

In WatiN, how to verify a table's column headers and rows?

Consider this HTML table:
<table id="build-table">
<tr>
<th>Build ID</th>
<th>Build Time</th>
</tr>
<tr>
<td>
5.1
</td>
<td>02.06.2011 13:33:03</td>
</tr>
</table>
How would I verify in WatiN that the table has the correct headers (Build ID and Build Time), and the correct content (in this case, one row containing the given hyperlink and date string)?
Sorry, we created a custom TableHandler, using the basic table building blocks: Here is the sample code:
public TableController(Regex tableControlId)
{ InitializeMembers(Find.ById(tableControlId), true); }
private void InitializeMembers(WatiN.Core.Constraints.AttributeConstraint tableControlId, bool hasColumnHeaders)
{
if (tableControlId == null)
{
throw new ArgumentNullException("tableControlId", "'tableControlId' passed in should not be null.");
}
WatiN.Core.Constraints.AttributeConstraint newTableControlId = tableControlId;
Assert.IsTrue(IE.Table(newTableControlId).Exists, "Table with id '" + newTableControlId.ToString() + "' does not exist on this page.");
_controlId = tableControlId;
_hasColumnHeaders = hasColumnHeaders;
_columnHeaders = (hasColumnHeaders) ? GetTableColumnHeaders() : null;
_totalRows = Table.TableRows.Count;
_totalColumns = GetAllColumnDataFromRow((TableRow)Table.TableRows[0], hasColumnHeaders).Count;
}
private StringCollection GetTableColumnHeaders()
{
return GetAllColumnDataFromRow((TableRow)Table.TableRows[0], true);
}
private StringCollection GetAllColumnDataFromRow(TableRow tableRow, bool isTableHeaderRow)
{
StringCollection RowValues = new StringCollection();
if (tableRow == null)
{
for (int colCounter = 0; colCounter < this.TotalColumns; colCounter++) RowValues.Add(String.Empty);
}
if (isTableHeaderRow)
{
foreach (Element e in tableRow.Elements)
{
if (e.TagName == "TH")
{
RowValues.Add(e.Text);
}
}
}
else
{
foreach (TableCell tc in tableRow.TableCells)
{
if (String.IsNullOrEmpty(tc.Text))
{
RowValues.Add(String.Empty);
}
else
{
RowValues.Add(tc.Text);
}
}
}
//fill up for the missing cells, if any, with blanks
int actualCellsInRow = tableRow.TableCells.Count;
int expectedCellsInRow = this.TotalColumns;
for (int colCounter = actualCellsInRow; colCounter < expectedCellsInRow; colCounter++)
{
RowValues.Add(String.Empty);
}
return RowValues;
}
Hope this helps.
There are at least three ways to do this:
I think Watin provides a tablehandler / tablecontroller related methods using which you can retrieve this information. You might want to search on that topic if you want to take this approach.
Using Xpath query (XML). Since you have the id of the table, you can use a XPath query to reach the node where your header is and verify that using a static string "Build ID" in your code. Same with the other pieces of information.
Regex - Using Regular Expressions, you can check if that text exists on the control / page.
If you view the source of your page, you will know the pattern that you should look for on the page. In fact, a simple Assert.AreEqual(true, new Regex("Build ID")Match.Success) should do the trick. However, this is a check that is purely done to see if the text exists on the page. You will not be looking at anything beyond that. Also, if you have multiple occurrences of the text then you should be considering the array of matches you get before you say, "yep, found it".
Note: You may have to checkout the syntax for using a Regex. The above information is just an abstract of what it would look like.
Cheers.