chrome.storage.local.get iterate simultaneously through keys and elements - loops

First of all, sorry if the original question isn't clear enough,
I was struggling to define the exact problem I'm having.
I am making a Chrome extension which features a list of blank input boxes. I would like to save the value assigned in those input boxes using the chrome.storage.set method, and retrieve said values into their original input boxes when the popup is reopened.
So far, I have managed to store locally the values of the boxes using a loop, assigning each value a key depending of its order of iteration.
HTML
<input type="text" class="random" value="">
<input type="text" class="random" value="">
<input type="text" class="random" value="">
<button id="4">save</button>
JS
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("4").addEventListener("click", save);
});
function save() {
var id = document.querySelectorAll("input[type='text']");
for (i = 0; i < id.length; i++) {
var inputValue= id[i].value;
if (id.length > 0) {
var key = "key"+i;
chrome.storage.local.set({[key]: inputValue});
alert(key)}
}
}
The problem comes when I try to retrieve each value and return it to its original input field. My solution was to create another loop which iterates through the input fields while retrieving the corresponding keys, but can't seem to make it work.
window.onload = () => {
const id = document.querySelectorAll("input[type='text']");
for (i = 0; i < id.length; i++) {
if (id.length > 0) {
var key = "key"+i;
chrome.storage.local.get([key], (data) => {
if (data.key) {
id[i].value = data.key;
}
});
}
}}
How should I define the variables properly? Is there any other work around to achieve the same result?

Related

preventing maxLength on a input type number

I took me a while to figure this one out, but can someone post a cleaner method for limiting the number of digits in an input type='number'. One of the issues is that errors are thrown if $scope.variable = null....meaning nothing in the input field.
<input type="number" model='modalName' ng-change="monitorLength('modalName',16)">
JS:
$scope.monitorLength = function (model,maxLength) {
if ($scope[model] != null) { // prevent error on empty input field
var len = $scope[model].toString() ; // convert to a string
if (len.length > maxLength) { //evaluate string length
$scope[model] = parseInt(len.substring(0, maxLength));
// convert back to number or warning is thrown on input value not being a number
}
}
}
I then needed to expand up on this to account for number only, preventing any non-digit characters include '.' and ',' symbols:
var reg = new RegExp(/^\d+$/) ;
$scope.monitorLength = function (modal,maxLength) {
if ($scope[modal] != null) {
var len = $scope[modal].toString() ;
if (len.length > maxLength) {
$scope[modal] = parseInt(len.substring(0, maxLength));
} else if (!reg.test(len)) {
$scope[modal] = parseInt(len.substring(0, len.length-2));
}
}
}
Is there way to extract the ng-modal that was responsible for calling the ng-change? so the call would only have to be: ng-change="monitorLength(10)". And then in the function somehow dynamically retrieve the calling ng-modal?
<input type="number" max="99" onkeypress="if (this.value.length >= 2) return false;"/>
OR
<!--maxlength="10"-->
<input type="number" onKeyPress="if(this.value.length==10) return false;" />
this is a cleaner method for limiting the number, using ngMaxlength for that:
<input type="number" model='modalName' ng-maxlength="16">
You can find more attributes and info here
Is there way to extract the ng-modal that was responsible for calling the ng-change?
Yes. You can define a directive and require the ngModelController.
.directive('maxNum', function(){
return {
require: '^ngModel',
link: function($scope, elem, attrs){
// here you can add formatters/parsers to the ngModel
// to affect the change on the ngModel.$viewValue.
}
}
})
As #rolinger stated on the other answer, using the built in directives will not prevent the use from entering non-valid characters, they simply mark the model as being invalid.

AngularJS dirPagination Select All Visible Rows

I'm building a CRUD data management project using Angular and the dirPagination directive and need to have a "select all" checkbox that selects all rows that are visible /without/ using jQuery.
I started with this - note - I am climbing the Angular learning curve and am aware that some/all of this may not be "the Angular way" but I also don't want to start fiddling (no pun) with the guts of dirPagination, ergo the dirPagination directive:
<tr dir-paginate-start="item in items|filter:filterFunction()|orderBy:sortPredicate:reverse| itemsPerPage: rowsPerPage">
and the individual row-checkbox
<input type="checkbox" class="rowSelector" ng-model="item.isSelected" ng-change="rowSelect($index, $event)"/> status: {{item.isSelected}}
and the related model elements:
$scope.items = [] //subsequently filled
$scope.rowsPerPage = 5;
$scope.rowSelect = function (ix, $event) {
var checked = (typeof $event == 'undefined') ? false : true;
if (!checked) { $scope.masterCheck = false; }
var rpp = $scope.rowsPerPage;
var p = $scope.__default__currentPage; //dirPagination's current page
var start = ((Math.max(0, p - 1) * rpp));
$scope.items[start + ix].isSelected = checked;
};
that works as expected. Check/uncheck a row and the {{item.isSelected}} value is updated in the model and is displayed beside the checkbox.
Then I added this /outside/ of the dirPagination repeat block:
<input type="checkbox" id="masterCheckbox" ng-model="masterCheck" ng-click="checkAll()" />
and the related function in the model:
$scope.masterCheck = false;
$scope.checkAll = function () {
var rpp = $scope.rowsPerPage;
var p = $scope.__default__currentPage; //dirPagination's current page
var start = ((Math.max(0, p - 1) * rpp));
var checked = $scope.masterCheck == true;
var rows = document.getElementsByClassName("rowSelector");
for (var ix = 0; ix < rows.length; ix++) {
rows[ix].checked = checked;
$scope.items[start + ix].isSelected = checked;
}
}
however in the checkAll() function checking/unchecking the individual rows isn't reflected in the {{item.isSelected}} display of each row.
Explicitly setting the individual item with
$scope.items[start + ix].isSelected = checked;
seems to set the 'isSelected' property of that item within the scope of the checkAll function but the row display does not change.
Clearly I have something wrong perhaps misunderstanding a Scope issue but at this point I'm stumped.
Any help greatly appreciated :-)
The light dawned, finally.
checkAll() as written tried to access each row by calculating its position using dir-paginate's __default__currentPage and Angular's row $index.
Of course that doesn't work because the items[] collection held by dir-paginate has been subjected to filtering and sorting, so while items[] do get checked (item.isSelected = true) the selected items/rows were living on non-visible pages. i.e. - we were selecting the wrong indexes.
One solution is comprised of the following -
The master checkbox
<input type="checkbox" id="masterCheckbox" ng-model="masterCheck" ng-click="checkAll()" />
The row checkbox (note function calls)
<input type="checkbox" class="rowSelector" value="{{sourceIndex('senId',item.senId)}}" ng-model="item.isSelected" ng-click="rowSelect(this)" />
the dir-paginate directive controls tag
<dir-pagination-controls on-page-change="onPageChange(newPageNumber)" max-size="15" direction-links="true" boundary-links="true" pagination-id="" template-url=""></dir-pagination-controls>
and the related $scope values and functions:
$scope.items = [];
$scope.masterCheck = false;
$scope.onPageChange = function (newPageNumber) {
//clear all selections on page change
//dir-paginate provides this hook
$scope.masterCheck = false;
for (var i in $scope.items) {
$scope.items[i].isSelected = false;
}
}
$scope.rowSelect = function (item) {
//if one is unchecked have to turn master off
if (!item.isSelected) $scope.masterCheck = false;
}
$scope.sourceIndex = function (keyName, key) {
//gets the actual items index for the row key
//see here http://stackoverflow.com/questions/21631127/find-the-array-index-of-an-object-with-a-specific-key-value-in-underscore
//for the 'getIndexBy' prototype extension
var ix = $scope.items.getIndexBy(keyName, key);
return ix;
}
$scope.checkAll = function () {
//only visible rows
var boxes = document.getElementsByClassName("rowSelector");
for (var bix in boxes) {
var ix = boxes[bix].value;
$scope.items[ix].isSelected = $scope.masterCheck;
}
}
There is probably a better way but while not highly efficient this one works well enough for common folk.
The $scope.sourceIndex() function stuffs the actual source row index into the row checkbox as its value= attribute value.
The checkAll() function then grabs all visible rows by the "rowSelector" class and then iterates through those grabbing the data index from the checkbox value and setting the appropriate item.isSelected.
The $scope.onPageChange is specified in the dir-Paginate controls directive and ensures that when the page changes, all row selections are cleared.
Happy happy.
Your variable masterCheck is being set to false in rowSelect() and subsequently in checkAll() you are setting checked to masterCheck which then is used to assign isSelected
This line is wrong:
var checked = $scope.masterCheck == true;
Because you want to flip masterCheck so it should be:
$scope.masterCheck = !$scope.masterCheck;
and then
.isSelected = $scope.masterCheck;
You weren't ever setting $scope.masterCheck to true so it was always false and since your isSelected values depended on it, they were always false. Also, this functions as a checkAll/unCheckAll to make it only check all change to the following:
$scope.masterCheck = !$scope.masterCheck;
var checked = $scope.masterCheck == true;

Apply Range Validation for Input Value AngularJS

When an user entering a value, system should check whether this value is within the range of Minimum and Maximum defined for this field. also, need check for number of decimal points allowed.
<input ng-model='data.value1' >
<input ng-model='data.value2' >
<input ng-model='data.value3' >
<input ng-model='data.value4' >
you can add type="number". and for angularJS
<input type="number"
ng-model=""
[name=""]
[min=""]
[max=""]
[required=""]
[ng-required=""]
[ng-minlength=""]
[ng-maxlength=""]
[pattern=""]
[ng-pattern=""]
[ng-change=""]>
Follow the link for more clarification
AngularJs Documentation
Extending My comment:
var range = 'your range';
var checkRange = function () {
var value = data.value;
if(value <=range) {
//your code;
} else {
//your code;
}
}
Update:
$scope.data.value = 500;
$scope.$watch('data.value', function (oldVal,newVal) {
if(newVal > 1000 ) {
$scope.data.value = 500;
}
})

Need to require only one of a group of fields with Parsley

I am using Parsley.js for validating a form submission on a project. One of my needs is to have Parsley require that at least one of three fields have data in them, and only fail validation if none of the three fields has data.
I am not sure from the documentation, how to accomplish this. I already have Parsley validation working on the rest of the form.
You can do that with a custom validator like so
var CheckReccursion = 0;
window.Parsley.addValidator('min3', {
validateString: function (value, requirement, instance) {
var notice =$('#notice').html(' ');
var group = $(requirement);//a class
var FieldsEmpty = 0;
var FieldsNotEmpty = 0;
var count = 0
group.each(function () {
var _val = $(this).val()
var length = _val.length
if (length > 0) {
FieldsNotEmpty++;
}
else {
FieldsEmpty++;
}
count++;
})
var isValid = (FieldsNotEmpty >=1)
//recursively execute
group.each(function (index) {
if (CheckReccursion === index) {
CheckReccursion++;
$(this).parsley().validate();
CheckReccursion = 0;
}
})
return isValid;
}
});
$(function () {
var ok=false;
var notice =$('#notice');
$('#form1').parsley().on('form:validated', function(formInstance) {
ok = formInstance.isValid({force: true});
})
.on('form:submit', function() {
if(!ok){
notice.html('Please fill at least 1 field');
return false;
}
else{
notice.html('okay');
return false;//change to true to submit form here
}
});
});
then add parsley attributes to the group of fields like so:
<form id="form1" data-parsley-validate="true">
<input type="text" name="field1"
data-parsley-min3 = ".group1"
data-parsley-min3-message = "At least 1 must be filled"
class="group1">
<input type="text" name="field2"
data-parsley-min3 = ".group1"
data-parsley-min3-message = "At least 1 must be filled"
class="group1">
<input type="text" name="field3"
data-parsley-min3 = ".group1"
data-parsley-min3-message = "At least 1 must be filled"
class="group1">
<span id="notice"></span>
<input type="submit" value="Submit">
</form>
Check out this fiddle https://jsfiddle.net/xcoL5Lur/6/
My advice is to add hidden checkbox element with the attribute:
data-parsley-mincheck="1"
now just add javascript code that checks the hidden checkbox attribute when your form input has value (and the opposite).
notice that you will need to add extra attribute to your hidden checkbox:
data-parsley-error-message="Please fill at least one input"
Another approach is to using data-parsley-group and the isValid({group,force}) method.
<input type="text" name="input1" data-parsley-group="group1">
<input type="text" name="input2" data-parsley-group="group2">
<input type="text" name="input3" data-parsley-group="group3">
$('#myform').parsley().on('form:validate', function (formInstance) {
if(formInstance.isValid({group: 'group1', force: true}) ||
formInstance.isValid({group: 'group2', force: true}) ||
formInstance.isValid({group: 'group3', force: true})) {
//do nothing
}
else {
$('#errorContainer').html('You must correctly fill at least one of these three groups!');
formInstance.validationResult = false;
}
});
you can add as many as parsley's attributes as you wish, like data-parsley-type="email" that will be validated when the given input is not empty.
we set the force: true because it it forces validation even on non-required.fields.
the html render for the errorContainer is needed because the isValid method does not affect UI nor fires events.

Prepare array of objects before passing it to Angularjs ng-repeat to avoid '10 $digest() iterations reached' error

I have a following ng-repeat in my view:
<div ng-repeat="field in fields">
{{field.someValue}}
</div>
The content of fields needs to be preprocessed before it is given to the view. So in my controller I have a function that loops through the fields object and adds some keys and removes some keys. A simplified pseudocode would look like that
myApp.controller('FieldsController', function($scope) {
$scope.fields = loadFieldsFromResource();
var i=0;
for(i = 0; i < $scope.fields.length; i++) {
if ($scope.fields[i].someProperty > maxValue) {
// Remove an item from the array
$scope.fields.splice(i,1);
}
else if ($scope.fields[i].someProperty < minValue) {
// Add an item to the array
$scope.fields.splice(i,0,createNewField());
}
}
})
Now this produces correct output but gives me the 10 $digest() iterations reached. error.
How can I make it work? (I only need the preprocessing done on init).
I've tried to copy the fields with angular.copy() to a temporary variable. Do the preprocessing on it and then assigning it to the fields variable but still I get the same error.
Is there a way of doing this sort of preprocessing outside of the Angular watch before I give it to the view?
Could you use ng-init?
<div ng-init="processFields()" ng-repeat="field in fields">
{{field.someValue}}
</div>
And then in your controller you could do the following:
myApp.controller('FieldsController', function ($scope) {
$scope.fields = [];
$scope.processFields = function () {
var fields = loadFieldsFromResource();
var i = 0;
for (i = 0; i < fields.length; i++) {
if (fields[i].someProperty > maxValue) {
// Remove an item from the array
fields.splice(i, 1);
}
else if (fields[i].someProperty < minValue) {
// Add an item to the array
fields.splice(i, 0, createNewField());
}
}
$scope.fields = fields;
}
})
I don't know your entire situation but I have few places where I need things loaded and sorted before I display.

Resources