Searching from DataTable - javascript

https://datatables.net/reference/api/search()#Example
Im using the link above as an example for my searching, but my setup is quite different.
<td>
<div class="plCell_desktop">
<input type="radio" class="" data-lnk_id="414107671" data-group="RUTH">
<label for="414107671">RUTH</label>
</div>
</td>
here is a extract from my table.
The only visable bit of data is "Ruth".
but when I search for say '76' it will still bring "Ruth" back as a result.
The reason is most likely the fact that I have a lot more info in the table cell than "Ruth".
Okay, so my question is. Can you force DataTables to search from the beginning of a word. e.g. ( enter "uth" will not bring back "Ruth", but "Ru" will, hope it makes sense ).
Can you do a kind of "innerHTML.val()" search with DataTables?

Okay, so my question is. Can you force DataTables to search from the
beginning of a word. e.g. ( enter "uth" will not bring back "Ruth",
but "Ru" will, hope it makes sense ).
Yes. Create a custom filter upfront that perform filtering like this. The default "smart search" will be overruled by the custom filter and any future filtering will go through that :
$.fn.dataTable.ext.search.push(function( settings, data, dataIndex ) {
var term = $('.dataTables_filter input').val().toLowerCase()
for (var i=0, l=data.length; i<l; i++) {
if (data[i].toLowerCase().indexOf(term) == 0 ) return true
}
return false
})
demo -> http://jsfiddle.net/qxcjzuxa/
As you may notice it would be very easy to make the dataTables filtering overall case sensitive (just an example). The filter array is a LIFO-structure where you can have multiple filters on top of each other. You remove a filter simply by $.fn.dataTable.ext.search.pop() if you for any reason will disable "beginning of word" filtering dynamically.

Could you just use jQuery?
$(".plCell_desktop label").each(function() {
// Using RexExp matching
RexExp regex = new RegExp(); // Your regex obj
if ($(this).val().match(regex)) {
// Do stuff if it matches
}
// Or if you just want to do something if it has a value:
if ($(this).val()) {
// ...
}
});

If you need to access the matching labels many times in your code, you may want to avoid computing the regular expression each time. One solution would be to run a one-time pre-processing that would add a custom attribute. Let's call it isOk.
You then can select the labels with a standard jQuery selector:
$('label[isOk=Y]')
Below is some demo code.
var regex = /^RU/;
$('label').each(function() {
$(this).attr('isOk', $(this).html().match(regex) !== null ? 'Y' : 'N');
});
var res = $('label[isOk=Y]');
// check whether we've selected the correct labels
res.each(function() { console.log($(this).attr('for')); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<td>
<div class="plCell_desktop">
<input type="radio" class="" data-lnk_id="414107671" data-group="RUTH">
<label for="414107671">RUTH</label>
<input type="radio" class="" data-lnk_id="414107672" data-group="RUTH">
<label for="414107672">RUTH TOO</label>
<input type="radio" class="" data-lnk_id="414107673" data-group="RUTH">
<label for="414107673">NOT REALLY RUTH</label>
</div>
</td>

Related

How to create a clickable list with multiple values in an element?

I am trying to create a list of names an user could chose from to select a object with multiple hidden values. I work with a PHP backend.
The code I wrote works but I think it is probably not the right way to approach the problem and could be written alot better, but I can't seem to find a better way.
Right now I print a <div> for every object which are clients in my case. Within the div I have four checkboxes that are hidden, which I check and uncheck on the background with a javascript function. The values of those checkboxes is what I need in javascript for an API call after the user choses the client.
I select and deselect the with a javascript function.
foreach($clients as $client) {
echo '<div class="'.$client->name.'-'.$client->id.' client-style" name="'.$client->name.'">
<input type="checkbox" class="'.$client->id.'" name="client_id" value="'.$client->id.'">
<input type="checkbox" class="'.$client->id.'" name="client_fb" value="'.$client->facebook.'">
<input type="checkbox" class="'.$client->id.'" name="client_insta" value="'.$client->instagram.'">
<input type="checkbox" " class="'.$client->id.'" name="client_wb" value="'.$client->website.'"></div>';
}
For every element I create an on click event handler
for (var i = 0; i < clientList.length; i++) {
const {name, id} = clientList[i];
$(`.${name}-${id}`).on('click', function() {
selectClientFromList({name, id});
});
}
I am trying to get a list of clickable "names". When a "name" is clicked, you want to get the "name" but also "id", "facebook", "instagram", "website".
Might be useful to use the <select> tag with multiple values like this but I don't want a dropdown. I need a scrollable list, because I also have use searchbar for this list.
With a lot of clients the html would grow fast. How do I clean my php code and keep the information about a client that the user selected?
Thanks in advance!
A good approach can be to use a hidden input. Give your div a class and then
foreach($clients as $client) {
echo '
<div class="'. $client->name.'-'.$client->id.' client-style" name="'.$client->name.'">
<input type="hidden" class="aclass '.$client->id.'" name="client_id" value="'.$client->id.'">
<input type="hidden" class="aclass '.$client->id.'" name="client_fb" value="'.$client->facebook.'">
<input type="hidden" class="aclass '.$client->id.'" name="client_insta" value="'.$client->instagram.'">
<input type="hidden" class="aclass '.$client->id.'" name="client_wb" value="'.$client->website.'"></div>';
}
And then instead of creating a click handler everytime. One works too.
$(`.aclass`).on('click', function() {
let type = $(this).attr('name'); // client_id or client_fb
let client_id = $(this).attr('class').replace("aclass",""); // $client->id's value is here
let value = $(this).val(); // credentials
});

Bind a new function to various elements on a page

This might be a dupe, but I couldn't find exactly what I was looking for. What I want is to bind a function to a named-class element on a page, then in later script, call a function against those regions. Specifically, I want to call something like (in the example below)...
$.each( $(".saveRegion"), function( idx, ele ){
var valsToSave = $(ele).getValuesToSave();
// shove the values to save into some construct
// but only for the controls inside the two divs classed as "saveRegion"
});
My page is a set of conditional includes in the Pug file. The reason that's important, is that the individual page includes might not have the same sets of controls, but that region, or parent div's children nodes, are considered a set of information. Think of a form with address information that might be a region, and gender, eye color, and height that might be a region, all included as separate Pug files if the form in question needs that information. The parent page just wants to ask all the regions "give me your information so I can compile and save it", and each of those includes should be able to respond to that.
I'm probably overdoing it, huh?
div.sampleRegions
if user.height == "tall"
include ./userTall.pug
if user.something == "another value"
include .anotherPage.pug
if user.hair == "brown"
include ./userBrownHair.pug
The html in question might look like:
<div id="sampleRegions">
<div class="saveRegion">
<input class="form-control" id="txtUserName" type="text">
<input class="form-control" id="txtPhone" type="text">
</div>
<div>
<input class="form-control" id="favoriteColor" type="text">
</div>
<div class="saveRegion">
<input class="form-control" id="txtCountry" type="text">
<input class="form-control" id="txtLanguage" type="text">
</div>
</div>
So, I want to bind a function like getValuesToSave() to a div, then write the specifics for that div's getValuesToSave() function. I'm using Pug (formerly Jade) to draw forms based on certain user-specific settings, so the page includes I'm using can each know how to get and return the data for their specific page sections via some prototypical function. I hope this is making sense.
This would be a simple matter of an abstract class or a function override in any other language that supports it. I wrote C# server side stuff for systems processing for like 15 years, and this is trivial there. I am sure I'm just missing something super simple. Thanks!
You are able to use jQuery plugins for this purpose.
Example is available here.
You could go with a simple function which looks for all kind of input elements inside a specified elements and maps their values to objects of type { [nameByAttribute]: value }.
Like this:
function getValues(selector, keyAttr) {
var INPUTS = ['textarea', 'input', 'select'].join();
var BOOL_INPUTS = ['checkbox', 'radio'];
var NUMBER_INPUTS = ['range', 'number'];
var regions = [].slice.call(document.querySelectorAll(selector));
return regions.map(function(region) {
var values = [].slice.call(region.querySelectorAll(INPUTS));
return values.map(function(element, index) {
var type = element.getAttribute('type');
var key = element.getAttribute(keyAttr) || index;
var value = element.value;
if(BOOL_INPUTS.indexOf(type) > -1) value = element.checked;
if(NUMBER_INPUTS.indexOf(type) > -1) value = +element.value;
return { [key]: value };
});
});
}
document.querySelector('#save').addEventListener('click', function() {
console.log(getValues('.saveRegion', 'id'));
});
<div id="sampleRegions">
<div class="saveRegion">
<input class="form-control" id="txtUserName" type="radio">
<input class="form-control" id="txtPhone" type="checkbox">
<input class="form-control" id="txtPhone" type="range">
<select idd="language">
<option>EN</option>
<option>DE</option>
<option>FR</option>
</select>
</div>
<div>
<input class="form-control" id="favoriteColor" type="text">
</div>
<div class="saveRegion">
<input class="form-control" id="txtCountry" type="text" value="textInput">
<textarea class="form-control" id="txtLanguage">textArea</textarea>
</div>
<button id="save">save</button>
</div>
This could be easily extended to take a mapping as input, which defines how certain inputs should be handled.
Ok, I think what I'm going to do is just add a function to each of the include pages that knows how to return an object containing the values for that region's salient controls, and name it based on the region name or something else to ensure uniqueness. Something like:
In Include-page-1
<div class="saveRegion" saveFunc="saveNameAddressRegion()">
<input id="val1" ..../>
<input id="val2" ..../>
<input..../>
</div>
<script>
function saveNameAddressRegion(){
// stupid-simple example
return { val1: $("#val1").val(), val2 : $("#val2").val() };
}
</script>
Or in Jade/Pug template:
.saveRegion(saveFunc="saveNameAddressRegion()")
input#val1(type="text")
input#val2(type="text")
input#val3(type="text")
script.
function saveNameAddressRegion(){
// stupid-simple example
return { val1: $("#val1").val(), val2 : $("#val2").val() };
}
Then in Include-page-2
<div class="saveRegion" saveFunc="doItAgain()">
<select id="selEyeColor">
<option value="blue">Blue</option>
<option value="green">Green</option>
<option value="brown">Brown</option>
</select>
</div>
<script>
function doItAgain(){
return { eyeColor : $("#selEyeColor option:selected").val() };
}
</script>
And then the enclosing/parent page can just do something like:
include ./Include-Page-1.pug
include ./Include-Page-2.pug
script.
$.each(".saveRegion", function( idx, ele ){
var vals = [];
vals.push( eval( $(ele).attr("saveFunc") ) );
// process them or whatever here.
});
This lets me get to a point where each Include file has a way of returning its values that may or may not be in some complicated on-screen form, control layout, etc. The parent page can always expect a simplified object with values, without knowing anything at all about each of the include-page layouts, design, contents, etc.
(( shrug ))

How to programmatically add ng-models for later use

I'm listing out several inputs based on another input like this like this:
<span ng-repeat="_ in ((_ = []) && (_.length=assistant.bends || 0) && _) track by $index" class="bendsInput">
#{{$index+1}} <br>
<input type="text" placeholder="ft" ng-change="calculateLength()"> ft
<input type="text" placeholder="in" ng-change="calculateLength()"> in
</span>
NOTE: the ng-repeat section might be really confusing, basically it's an ng-repeat between 0 and whatever the value of assistant.bends is the $index variable is the current index of the loop.
I need to add an ng-model to both the ft and in textboxes so that I can access them (the value of assistant.bends is dynamic and not fixed.
I'm thinking something like this: ng-model="assistant.bend{{$index}}.ft"
So that the final result will be ng-model="assistant.bend1.ft"
Then in my javascript I can loop through it and add all the feet and inches together.
var totalFeet = 0;
for(i=0; i<assistant.bends; i++) {
totalFeet += assistant.bend+i+.ft;
}
I'm not sure on the correct syntax for something like this.
Some advice on dynamic models: If you need a complex structure, use one, rather than repeating for a number of times based on a number
Ex:
// your JS needs a more complex structure
$scope.assistant = {
// bends: 5 // not great, you can use this value to generate objects such as below
bends: [ {ft: '', in: ''}, {ft: '', in: ''}, {ft: '', in: ''} ]
}
// if bends value changes, update the array
HTML
<span ng-repeat="bend in assistant.bends track by $index" class="bendsInput">
#{{$index+1}} <br>
<input type="text" placeholder="ft" ng-model="bend.ft" ng-change="calculateLength()"> ft
<input type="text" placeholder="in" ng-model="bend.in" ng-change="calculateLength()"> in
</span>
<div class='sum' ng-bind="totalFeet"></div> <!-- this will update each time your function for summation runs -->
This way, your ng-model gets assigned to whatever attribute is on the structure. You can even create new keys dynamically, though I don't recommend it for the sake of consistency.
Then your loop can be about the same:
$scope.totalFeet = 0, totalInches = 0;
for(i=0; i<$scope.assistant.bends.length; i++) {
$scope.totalFeet += assistant.bends[i].ft;
totalInches += assistant.bends[i].in;
}
$scope.totalFeet += (totalInches / 12);

How to filter multiple dynamic values in angularJS

Just like the question provider in This question, I am building a filter that filters on multiple values.
However, in my situation, I also want to have the filters created dynamically from an array.
So, for this example, instead of
<input type="checkbox" ng-model="genrefilters.action" />Action
<input type="checkbox" ng-model="genrefilters.family" />Family
I want to ng-repeat like this to create the input boxes:
<div ng-repeat="distinctgenre in distinctgenres'">
<input type="checkbox" ng-model="genrefilters.distinctgenre"/{{distinctgenre}}
</div>
However, when I do this, my filter, which is the same as in the original question:
.filter('bygenre', function () {
return function (movies, genrefilters) {
var items = {
genrefilters: genrefilters,
out: []
};
angular.forEach(movies, function (value, key) {
if (this.genrefilters[value.genre] === true) { // <=== THIS LINE ERRORS OUT
this.out.push(value);
}
}, items);
return items.out;
};
Errors out, with TypeError: Cannot read property 'Action' of undefined.
I am using the filter like so.
<tr ng-repeat="movie in displayedmovies | byCohort:genrefilters">
{{movie}}
Any assistance would be appreciated.
Thanks to dfsq for the reminder on bracket notation. That seems to be the main issue. (I also had some other dumb naming convention issues)
You should generate filter inputs using bracket notation:
<div ng-repeat="distinctgenre in distinctgenres'">
<input type="checkbox" ng-model="genrefilters[distinctgenre]" /> {{distinctgenre}}
</div>

add templated div to page multiple times with different ids

I am using ASP.Net MVC along with Jquery to create a page which contains a contact details section which will allow the user to enter different contact details:
<div id='ContactDetails'>
<div class='ContactDetailsEntry'>
<select id="venue_ContactLink_ContactDatas[0]_Type" name="venue.ContactLink.ContactDatas[0].Type">
<option>Email</option>
<option>Phone</option>
<option>Fax</option>
</select>
<input id="venue_ContactLink_ContactDatas[0]_Data" name="venue.ContactLink.ContactDatas[0].Data" type="text" value="" />
</div>
</div>
<p>
<input type="submit" name="SubmitButton" value="AddContact" id='addContact' />
</p>
Pressing the button is supposed to add a templated version of the ContactDetailsEntry classed div to the page. However I also need to ensure that the index of each id is incremented.
I have managed to do this with the following function which is triggered on the click of the button:
function addContactDetails() {
var len = $('#ContactDetails').length;
var content = "<div class='ContactDetailsEntry'>";
content += "<select id='venue_ContactLink_ContactDatas[" + len + "]_Type' name='venue.ContactLink.ContactDatas[" + len + "].Type'><option>Email</option>";
content += "<option>Phone</option>";
content += "<option>Fax</option>";
content += "</select>";
content += "<input id='venue_ContactLink_ContactDatas[" + len + "]_Data' name='venue.ContactLink.ContactDatas[" + len + "].Data' type='text' value='' />";
content += "</div>";
$('#ContactDetails').append(content);
}
This works fine, however if I change the html, I need to change it in two places.
I have considered using clone() to do this but have three problems:
EDIT: I have found answers to questions as shown below:
(is a general problem which I cannot find an answer to) how do I create a selector for the ids which include angled brackets, since jquery uses these for a attribute selector.
EDIT: Answer use \ to escape the brackets i.e. $('#id\\[0\\]')
how do I change the ids within the tree.
EDIT: I have created a function as follows:
function updateAttributes(clone, count) {
var f = clone.find('*').andSelf();
f.each(function (i) {
var s = $(this).attr("id");
if (s != null && s != "") {
s = s.replace(/([^\[]+)\[0\]/, "$1[" + count + "]");
$(this).attr("id", s);
}
});
This appears to work when called with the cloned set and the count of existing versions of that set. It is not ideal as I need to perform the same for name and for attributes. I shall continue to work on this and add an answer when I have one. I'd appreciate any further comments on how I might improve this to be generic for all tags and attributes which asp.net MVC might create.
how do I clone from a template i.e. not from an active fieldset which has data already entered, or return fields to their default values on the cloned set.
You could just name the input field the same for all entries, make the select an input combo and give that a consistent name, so revising your code:
<div id='ContactDetails'>
<div class='ContactDetailsEntry'>
<select id="venue_ContactLink_ContactDatas_Type" name="venue_ContactLink_ContactDatas_Type"><option>Email</option>
<option>Phone</option>
<option>Fax</option>
</select>
<input id="venue_ContactLink_ContactDatas_Data" name="venue_ContactLink_ContactDatas_Data" type="text" value="" />
</div>
</div>
<p>
<input type="submit" name="SubmitButton" value="AddContact" id='addContact'/>
</p>
I'd probably use the Javascript to create the first entry on page ready and then there's only 1 place to revise the HTML.
When you submit, you get two arrays name "venue_ContactLink_ContactDatas_Type" and "venue_ContactLink_ContactDatas_Data" with matching indicies for the contact pairs, i.e.
venue_ContactLink_ContactDatas_Type[0], venue_ContactLink_ContactDatas_Data[0]
venue_ContactLink_ContactDatas_Type[1], venue_ContactLink_ContactDatas_Data[1]
...
venue_ContactLink_ContactDatas_Type[*n*], venue_ContactLink_ContactDatas_Data[*n*]
Hope that's clear.
So, I have a solution which works in my case, but would need some adjustment if other element types are included, or if other attributes are set by with an index included.
I'll answer my questions in turn:
To select an element which includes square brackets in it's attributes escape the square brackets using double back slashes as follows: var clone = $("#contactFields\[0\]").clone();
& 3. Changing the ids in the tree I have implemented with the following function, where clone is the variable clone (in 1) and count is the count of cloned statements.
function updateAttributes(clone, count) {
var attribute = ['id', 'for', 'name'];
var f = clone.find('*').andSelf();
f.each(function(i){
var tag = $(this);
$.each(attribute, function(i, val){
var s = tag.attr(val);
if (s!=null&& s!="")
{
s = s.replace(/([^\[]+)\[0\]/, "$1["+count+"]");
tag.attr(val, s);
}
});
if ($(this)[0].nodeName == 'SELECT')
{ $(this).val(0);}
else
{
$(this).val("");
}
});
}
This may not be the most efficient way or the best, but it does work in my cases I have used it in. The attributes array could be extended if required, and further elements would need to be included in the defaulting action at the end, e.g. for checkboxes.

Categories