Angular custom sort with multiple parameters and random value - javascript

I know various variations of this have been asked multiple times before, but nevertheless I am having trouble with it. I have a small angular app wherein I am comparing two characters (it's a D&D initiative roll). I am looking to sort the ng-repeat in the following code:
var dmTools = angular.module('dmTools', []);
dmTools.controller('initTracker', function($scope, $http) {
var charInit = function() {
this.name = "";
this.mod = 0;
this.init = 0;
}
$scope.characters = [
];
$scope.character = new charInit();
$scope.addChar = function() {
$scope.characters.push($scope.character);
$scope.character = new charInit();
}
$scope.deleteChar = function(character) {
$scope.characters.splice($scope.characters.indexOf(character), 1);
}
$scope.charSort = function(a, b) {
//first we go by initiative roll
if (a.init < b.init) {
return -1;
}
if (a.init > b.init) {
return 1;
}
//if initiative rolls are the same, go by initiative mod
if (a.mod < b.mod) {
return -1;
}
if (a.mod > b.mod) {
return 1;
}
//if both are the same, roll off until one gets a higher roll than the other.
var aRoll = 0;
var bRoll = 0;
while (aRoll == bRoll) {
aRoll = Math.floor(Math.random() * 20) + 1;
bRoll = Math.floor(Math.random() * 20) + 1;
if (aRoll < bRoll) {
return -1;
}
if (aRoll > bRoll) {
return 1;
}
}
//while this should not be possible to reach, we'll put it in for safeties sake.
return 0;
};
$scope.rollInit = function() {
for (x in $scope.characters) {
$scope.characters[x].init = Math.floor(Math.random() * 20) + 1 + $scope.characters[x].mod;
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="app" id="initTracker" data-ng-controller="initTracker">
<h3>Initiative Tracker</h3>
<div class="form-group">
<label>Name
<input type="text" data-ng-model="character.name" class="form-control" />
</label>
</div>
<div class="form-group">
<label>Mod
<input type="number" data-ng-model="character.mod" class="form-control" />
</label>
</div>
<div>
<input class="btn btn-primary" style="margin:5px;" data-ng-click="addChar()" type="button" value="Add Character" />
<input class="btn btn-success" style="margin:5px;" data-ng-click="rollInit()" type="button" value="Roll Initiative" />
</div>
<table class="table table-striped">
<tr>
<th>Name</th>
<th>Modifier</th>
<th>Initiative</th>
<th>Delete</th>
</tr>
<tr data-ng-repeat="character in characters | orderBy: charSort">
<td data-ng-bind="character.name"></td>
<td data-ng-bind="character.mod"></td>
<td data-ng-bind="character.init"></td>
<td>
<input type="button" class="btn btn-danger" value="Delete" data-ng-click="deleteChar(character)" />
</td>
</tr>
</table>
</div>
My charSort function is a working sort that could easily be passed to an array.sort(function) sort of call, but angular is only passing in 1 character object to the function. How can I get a proper custom comparison based sort of this variety in an angular template?

Do you need to sort the array in the data-ng-repeat? You could just sort the array in rollInit().
If anything changes the array with inserts or deletes, you could $watchCollection on the array and re-sort as things are added or removed.
Then you can do any kind of sorting you like in Javascript, and the data-ng-repeat will always see the data in the correct order.

if you have three property for an object , you can sort them in priority of property A , property B and property C like below
<tr data-ng-repeat="character in characters | orderBy:['A','B','C']">

Related

Filtering js array with underscore.js

I'm new to Java Script and completely not into front end. I have a problem how to display filtered array in html template.
What I have is:
<form>
<div class="form-group">
<input v-model="stitle" type="text" id=inputTitle/>
</div>
<div class="form-group">
<input v-model="scast" type="text" id="inputCast"/>
</div>
<div class="form-group row">
<input #click="messageFiltering" type="button" class="btn btn-info col-sm-12" value="Szukaj"/>
</div>
</form>
<table>
<thead>
<tr>
<th>Title</th>
<th>Production Year</th>
<th>Cast</th>
<th>Genres</th>
</tr>
</thead>
<tbody>
<tr v-for="movie in messageFiltering">
<td>{{movie.title}}</td>
<td>{{movie.year}}</td>
<td>{{movie.cast.toString()}}</td>
<td>{{movie.genres.toString()}}</td>
</tr>
</tbody>
</table>
And my script:
<script type="module">
var app = new Vue({
el: '#app',
data: {
stitle: '',
syearfrom: '',
syearto: '',
scast: '',
movies: [
{"title":"Chained for Life","year":1951,"cast":["Hilton Twins"],"genres":[]},
{"title":"Cheese Chasers","year":1951,"cast":["Looney Tunes"],"genres":["Animated"]},
{"title":"Chicago Calling","year":1951,"cast":["Dan Duryea","Mary Anderson"],"genres":["Noir"]},
{"title":"China Corsair","year":1951,"cast":["Jon Hall","Ernest Borgnine"],"genres":["Adventure"]},
{"title":"So This Is Paris","year":1955,"cast":["Tony Curtis","Gloria DeHaven"],"genres":["Musical"]},
{"title":"Soldier of Fortune","year":1955,"cast":["Clark Gable","Susan Hayward"],"genres":["Drama"]},
{"title":"Son of Sinbad","year":1955,"cast":["Dale Robertson","Sally Forrest","Vincent Price"],"genres":["Adventure"]},
{"title":"Southbound Duckling","year":1955,"cast":["Tom and Jerry"],"genres":["Animated"]},
{"title":"Special Delivery","year":1955,"cast":["Joseph Cotten","Eva Bartok"],"genres":["Comedy"]}
],
},
methods: {
messageFiltering() {
let collection = _.filter(movies, function (element) {
return element.year.toString().toLowerCase().indexOf(this.syearfrom.toString().toLowerCase()) != -1;
});
collection = _.filter(collection, function (element) {
return element.title.toString().toLowerCase().indexOf(this.stitle.toString().toLowerCase()) != -1;
});
collection = _.filter(collection, function (element) {
return element.cast.toString().toLowerCase().indexOf(this.scast.toLowerCase().toLowerCase()) != -1;
});
return collection;
}
}
})
I would like to display the array movies filtered by messageFiltering() by the user input from form in my table But I have no idea how to connect it. Is the button #click event alright?
I am not sure what you want to accomplish, just shorten your code first. Secondaly, You do not need lodash for this basic filter, you can use default filter to do this task.
Also there seems to be multiple toLowerCase() instead of toString().toLowerCase()
collection = _.filter(collection, function (element) {
return element.cast.toString().toLowerCase().indexOf(this.scast.toString().toLowerCase()) != -1;
});
return collection;
Maybe this helps you, if not please edit the question so that we could understand it better what you want to accomplish and not this redundant code.
Just refactoring your code method
messageFiltering() {
return movies.filter(movies, function (element) {
return element.year.toString().toLowerCase().indexOf(this.syearfrom.toString().toLowerCase()) != -1 && element.title.toString().toLowerCase().indexOf(this.stitle.toString().toLowerCase()) != -1 && element.cast.toString().toLowerCase().indexOf(this.scast.toStringe().toLowerCase()) != -1;
});
}
Edit :
Try updating this line
<input v-on:click="messageFiltering" type="button" class="btn btn-info col-sm-12" value="Szukaj"/>
Ref

How to assign reassign checkbox ng-model on ng-repeat

Please help me out. I have a checkboxes with models defined. I am displaying checkboxes and using the model to set if the checkbox is selected or not. Below is the code for setting the ng-model.
LoadValues(obj) {
vm.index = false;
vm.create = false;
vm.edit = false;
vm.delete = false;
vm.other = false;
var pList = obj.Functions;
var currentModule = obj.Name;
for (var i = 0; i < pList.length; i++) {
var currentItem = pList[i];
console.log(currentItem)
if (currentItem.search("Index") > 0) {
vm.index = true;
console.log(vm.index);
} else if (currentItem.search("Create") > 0) {
vm.create = true;
} else if (currentItem.search("Edit") > 0) {
vm.edit = true;
} else if (currentItem.search("Delete") > 0) {
vm.delete = true;
} else if (currentItem.search("Other") > 0) {
vm.other = true;
}
}
}
Below is the check boxes.
<tbody>
<tr ng-repeat="item in list">
<td>
{{item.Name}}
</td>
<td>
<input id="Index" type="checkbox" ng-model="vm.index" ng-click="EditRole(Right,item.Module,'Index')">
</td>
<td>
<input id="Create" type="checkbox" ng-model="vm.create" ng-click="EditRole(item.Role,'Create')">
</td>
<td>
<input id="Edit" type="checkbox" ng-model="vm.edit" ng-click="EditRole(item.Role,item.Module,'Edit')">
</td>
<td>
<input id="Delete" type="checkbox" ng-model="vm.delete" ng-click="EditRole(item.Role,item.Module,'Delete')">
</td>
<td>
<input id="Other" type="checkbox" ng-model="vm.other" ng-click="EditRole(item.Role,item.Module,'Other')">
</td>
</tr>
</tbody>
The problem is it assigns the same ng-model to all the items in the list. I have tried to find solutions nothing is helping. Your help would be very much appreciated.
i am reading my data from a json file. Below is some example data:
[
{"Role":"Staff","Admins":[{"Name":"Username","userRights":["UserEdit","UserCreate"]
}]
The easiest way to use ng-model on a checkbox is to pass it an abject. The code below converts an array of items into an object for the checkboxes.
I created a variable called $scope.userRights which contains all of the available options.
In the HTML we loop though each field displaying its name and then loop though all of the userRights.
The submit button then converts the object back into the array format we received.
HTML
<div ng:controller="MainCtrl">
<button ng-click="submit()">Submit</button>
<table>
<tr ng-repeat="field in fields">
<td ng-bind="field.Name"></td>
<td ng-repeat="right in userRights">
<label>
<input type="checkbox" ng-model="field.userRights[right]" /> {{right}}
</label>
</td>
</tr>
</table>
<pre ng-bind="fields | json"></pre>
</div>
JavaScript
app.controller('MainCtrl', function($scope) {
$scope.userRights = ["UserEdit","UserCreate","UserSomethingElse"];
$scope.fields = [
{"Name":"Username","userRights":["UserEdit","UserCreate"]},
{"Name":"Password","userRights":["UserEdit"]}
];
// Convert array to object
$scope.fields.forEach(function(field) {
var res = {};
field.userRights.forEach(function(right) {
res[right] = true;
});
field.userRights = res;
});
function objectValues(obj) {
var res = [];
var keys = Object.keys(obj);
for (var i=0; i<keys.length; i++) {
if (obj[keys[i]]) res.push(keys[i]);
}
return res;
}
// Convert object to array
$scope.submit = function() {
$scope.fields.forEach(function(field) {
field.userRights = objectValues(field.userRights);
});
};
});
Demo
Your ng-model has to be like so:
ng-model="item.index"
And then in your controller inside the for loop:
current_item.index = true;
Hope it helps =)

AngularJS: Filter by regex

I want to filter a list of elements by regex (user will type a regex in the input search field) using AngularJS filter.
I wrote everything that seemed to be necessary for me, but I can't manage to make the regex work correctly.
Here is what I've done so far:
View.html
<div ng-controller="listCtrl">
<input type="text" placeholder="search by any" ng-model="search.$">
<input type="text" placeholder="search by fist name" ng-model="search.fname">
<input type="text" placeholder="search by last name" ng-model="search.lname" >
<input type="text" placeholder="search by tel" ng-model="search.tel" >
<input type="text" placeholder="search by date" ng-model="search.date">
<table>
<tr ng-repeat="user in users|regex : search as res">
<td>{{user.fname}}</td>
<td>{{user.lname.toUpperCase()}}</td>
<td>{{user.tel}}</td>
<td>{{user.date | date: 'EEE'}}</td>
</tr>
<tr ng-if="res.length < 1">
<td>No elements found...</td>
</t>
</table>
</div>
app.js
...
app.filter('regex', function() {
return function(input, property) {
var patt;
var field;
var out = [];
if(input === undefined || property === undefined) {
return out;
}
angular.forEach(property, function(key, value) {
patt = new RegExp(key);
field = value;
});
for (var i = 0; i < input.length; i++){
if(patt.test(input[i][eval(field)]))
out.push(input[i]);
}
return out;
};
});
...
The listCtrl will just add some elements to $scope.
What is wrong here?
You should just be able to call a function as part of your ng-repeat filter expression, ie.
then in your controller you'd have something like:
function textRegEx(row) {
var regex = new RegExp("row");
return regex.test(row)
}
This is for the entire row. You'd have to adapt it to work on just one of the fields. I posted a codepen earlier today here: http://codepen.io/anon/pen/yOjdJV?editors=1010 with a dropdown for selecting the field you could adapt. Obviously if you're enabling searching on multiple fields you could concatenate the calls to regex.test into a single line.

push increment item into an array in Angularjs

http://plnkr.co/edit/NDTgTaTO1xT7bLS1FALN?p=preview
<button ng-click="addRow()">add row</button>
<div ng-repeat="row in rows">
<input type="text" placeholder="name"><input type="tel" placeholder="tel">
</div>
I want to push new row and save all the fields but now I'm stuck at adding new rows. How to know the current number of row and do increment to push into the array?
Look at this example I created which allows you to generate up to eight unique input fields for Telephone and Text Entries.
var app = angular.module("MyApp", []);
app.controller("MyCtrl", function($scope) {
$scope.rows = [];
var Row = function(tel, text) {
// Private data
var private = {
tel: tel,
text: text
}
// Expose public API
return {
get: function( prop ) {
if ( private.hasOwnProperty( prop ) ) {
return private[ prop ];
}
}
}
};
$scope.addRow = function(){
if($scope.rows.length < 8){
var newItemNum = $scope.rows.length + 1;
var row = new Row('item' + newItemNum, 'item' + newItemNum);
$scope.rows.push(row);
}
};
$scope.saveAll = function(){
// $scope.result = 'something';
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="MyCtrl">
<h2>Setting</h2>
<button ng-click="addRow()">Add Row</button>
<br />
<div ng-repeat="row in rows">
<input type="text" placeholder="Text" ng-model="row.textModel" >
<input type="tel" placeholder="Phone" ng-model="row.telModel" >
</div>
<br />
{{rows}}
</div>
</div>
Move functions inside controller 'Ctrl'.
In your script:
function Ctrl($scope) {
$scope.result = "something";
$scope.rows = ['1'];
$scope.addRow = function(){
if ($scope.rows.length < 8) {
$scope.rows.push($scope.rows.length + 1);
}
}
$scope.saveAll = function(){
// $scope.result = 'something';
}
}

How do I loop through children objects in javascript?

I have this code in a function:
tableFields = tableFields.children;
for (item in tableFields) {
// Do stuff
}
According to a console.log of tableFields, I am getting an array back as I assume I need to do. A console.log of item within the loops returns undefined. What do I have to do to loop through tableFields and insert each object into a table?
console log of tableFields:
HTMLCollection[label, input, label, input 25, label, input, input, input Remove]
0
label
1
input
2
label
3
input 25
4
label
5
input
6
input
7
input Remove
description[]
input
hours[]
input
invoice_number
input
getlength
8
rate[]
input 25
item
item()
iterator
iterator()
namedItem
namedItem()
__proto__
HTMLCollectionPrototype { item=item(), namedItem=namedItem(), iterator=iterator()}
Here is the entire section of code as I have so far:
$this->title("Test");
$this->defaultMenu();
$select = "";
$names = Customer::getNames();
foreach ($names as $id => $name) {
$select .= '<option value="'.$id.'"';
if ($this->customerid == $id) $select .= ' selected ';
$select .= '>'.$name.'</option>';
}
$form = '
<script type="text/javascript">
var counter = 0;
function isEven(int){
int = Number(int);
return (int%2 == 0);
}
function moreLabor() {
var table = document.getElementById("editTable");
var tableFields = document.getElementById("readroot");
tableFields = tableFields.children;
console.log(tableFields);
for (item in tableFields) {
if (isEven(counter)) {
var tableRow = table.insertRow(-1);
var label = tableRow.insertCell(-1);
console.log(tableFields[item]);
label.appendChild(tableFields[item]);
} else {
var field = tableRow.insertCell(-1);
field.innerHTML = item.innerHTML;
}
counter++;
}
console.log();
var insertHere = document.getElementById("writeroot");
}
window.onload = function(){
document.getElementById(\'moreLabor\').onclick = function(){ moreLabor(); }
moreLabor();
}
</script>
<div id="readroot" style="display: none">
<tr>
<td><label for="hours">Hours:</label></td>
<td><input type="text" name="hours[]" value="" /></td>
</tr>
<tr>
<td><label for="rate">Rate:</label></td>
<td><input type="text" name="rate[]" value="25" /></td>
</tr>
<tr>
<td><label for="description">Description:</label></td>
<td><input type="text" name="description[]" value="" /></td>
</tr>
<input type="hidden" name="invoice_number" value="'.$this->number.'" />
<tr>
<td><input type="button" value="Remove"
onclick="this.parentNode.parentNode.removeChild(this.parentNode);" /></td>
</tr>
</div>
<form method="POST" class="invoice" id="edit">
<table id="editTable">
<tr>
<td><label>Work Order Number:</label></td>
<td><input type="text" name="number" value="'.$this->number.'"/></td>
</tr>
<tr>
<td><label>Customer:</label></td>
<td><select name="customerid">'.$select.'</select></td>
</tr>
<span id="writeroot"></span>
<tr>
<td><input type="button" id="moreLabor" value="Add labor"/></td>
<td><input type="submit" name="Save" value="Save" /></td>
</tr>';
if (!is_null($this->id)) {
$form .= '<input type="hidden" name="id" value="'.$this->id.'"/>';
}
$form .= '</table></form>';
$this->component($form);
The trick is that the DOM Element.children attribute is not an array but an array-like collection which has length and can be indexed like an array, but it is not an array:
var children = tableFields.children;
for (var i = 0; i < children.length; i++) {
var tableChild = children[i];
// Do stuff
}
Incidentally, in general it is a better practice to iterate over an array using a basic for-loop instead of a for-in-loop.
In ECS6, one may use Array.from() or Spread array syntax:
const listItems = document.querySelector('ul').children;
const listArray = Array.from(listItems);
// or
const listArray = [...listItems];
listArray.forEach((item) => {console.log(item)});
if tableFields is an array , you can loop through elements as following :
for (item in tableFields); {
console.log(tableFields[item]);
}
by the way i saw a logical error in you'r code.just remove ; from end of for loop
right here :
for (item in tableFields); { .
this will cause you'r loop to do just nothing.and the following line will be executed only once :
// Do stuff
Modern JS also uses the for..of to enable us to iterate DOM children objects, array, or other iterable objects. I think it is very clean and simple.
var children = tableFields.children;
for (c of children) {
console.log(c);
// Do stuff with child c
}
The backwards compatible version (IE9+) is
var parent = document.querySelector(selector);
Array.prototype.forEach.call(parent.children, function(child, index){
// Do stuff
});
The es6 way is
const parent = document.querySelector(selector);
Array.from(parent.children).forEach((child, index) => {
// Do stuff
});
Using ES6,
[...element.children].map(child => console.log(child));
In the year 2020 / 2021 it is even easier with Array.from to 'convert' from a array-like nodes to an actual array, and then using .map to loop through the resulting array.
The code is as simple as the follows:
Array.from(tableFields.children).map((child)=>console.log(child))
I’m surprised no-one answered with this code:
for(var child=elt.firstChild;
child;
child=child.nextSibling){
do_thing(child);
}
Or, if you only want children which are elements,
this code:
for(var child=elt.firstElementChild;
child;
child=child.nextElementSibling){
do_thing(child);
}

Categories

Resources