I am invoking a function when we click on the checkbox and based on that clicking, I am pushing all the invoices which have pdf from a for loop by calling the url based on some row selected in the form. I console log the array and i am able to find the available invoices array, but somehow that array gets empty when i have a condition that if we have content in the array, we show a message. Is there something i am missing. Here is my code
isExist brings true or false. If its true we push that invoice in availableInvoicesPdf
if its false we push that value in noAvailableInvoicesPdf. itemChecked is ng-model binded with the checkbox
onChange() {
this.availableInvoicesPdf = [];
this.noAvailableInvoicesPdf = [];
this.allPdfResponseAvailable = false;
for (let i = 0; i < this.invoices.length; i += 1) {
// eslint-disable-next-line no-loop-func
this.Invoice.hasPdf(this.invoices[i].id).then((isExists) => { // calls a function which have a function hasPdf and takes id of the invoices selected to return back the response
if (isExists) {
this.availableInvoicesPdf.push({ //pushing invoice id and number for the invoices which have pdf available. I made sure that it had a pdf everytime.
id: this.invoices[i].id,
number: this.invoices[i].number
});
} else {
this.noAvailableInvoicesPdf.push({
number: this.invoices[i].number,
});
}
// i dont have value for available invoices pdf here. Somethinf weird if i console log it then i am able to see it, but when i debug, the array is empty.
});
}
this.showSuccessMessage = false;
this.noInvoiceMessage = false;
if (this.itemChecked && this.availableInvoicesPdf.length === 0 && this.noAvailableInvoicesPdf.length === 0) { // i dont have value for available invoices pdf here
this.checkInvoicesHasPdf();
console.log('after running invoice pdf', this.availableInvoicesPdf);
if (this.availableInvoicesPdf.length > 0 && this.noAvailableInvoicesPdf.length > 0) { // i dont have value for available invoices pdf here
this.showSuccessMessage = true;
this.noInvoiceMessage = true;
} else if (this.availableInvoicesPdf.length > 0 && this.noAvailableInvoicesPdf.length === 0) { // i dont have value for available invoices pdf here
this.showSuccessMessage = true;
} else if (this.availableInvoicesPdf.length === 0 && this.noAvailableInvoicesPdf.length > 0) { // i dont have value for available invoices pdf here
this.noInvoiceMessage = true;
}
} else {
this.attachDefaultInvoice = this.itemChecked;
}
}
Here is the hasPDF Code:
hasPdf(id) {
const url = `${this.ARConstant.EXPORTING_REST_API_URL}pdf/${id}`;
return this.$http.head(url)
.then(() => true)
.catch(() => false);
}
HTML:
<div class="pane-content" layout="column" ng-if="$ctrl.invoices.length <= 100">
<div class="form-row" layout="row">
<div class="attachPdf" flex="20">{{ 'attachPdf' | translate }}</div>
<md-checkbox ng-disabled="$ctrl.isItaly" aria-label="{{ 'toAttach'| translate }}"
class="attachPdf-cb md-primary" ng-model="$ctrl.itemChecked" ng-change="$ctrl.onChange()">
</md-checkbox>
<p ng-if="$ctrl.itemChecked && !$ctrl.allPdfResponseAvailable">{{ 'waitPdfUpload' | translate }} {{$ctrl.invoicesPdf.length}} /
{{$ctrl.invoices.length}}</p>
</div>
</div>
<div class="pane-content" layout="column" ng-if = "$ctrl.showSuccessMessage">
<div class="form-row" layout="row">
<div class="success-box-header-hidden" flex="20"></div>
<div flex="80" class="success-message">
<p class="success-text">
<md-icon class="ionicons-svg-md-checkmark-circle" md-svg-src="img/ionicons-svg-md-checkmark-circle.svg"></md-icon>
<span></span>Invoices PDF uploaded successfully</p>
</div>
</div>
</div>
Related
I am making a to do list, and to do items are being lost (as expected) when page is refreshed. How do I stop it from clearing the items when the page is refreshed???????
Html:
<div class="row">
<input id="userInput" type="text" placeholder="New item..." maxlength="190"autofocus>
<button id="enter">Add</button>
</div>
<div class="row" id="items">
<div class="listItems col-12">
<ul class="col-12 offset-0 col-sm-8 offset-sm-2">
</ul>
</div>
</div>
</div>
JS:
function createListElement() {
var li = document.createElement("li");
li.appendChild(document.createTextNode(input.value));
ul.appendChild(li);
function crossOut() {
li.classList.toggle("done");
}
li.addEventListener("click",crossOut);
var dBtn = document.createElement("button");
dBtn.appendChild(document.createTextNode("X"));
li.appendChild(dBtn);
dBtn.addEventListener("click", deleteListItem);
function deleteListItem(){
li.classList.add("delete");
}
}
//enter works
function addListAfterKeypress(event) {
if (inputLength() > 0 && event.which ===13) {
createListElement();
}
}
input.addEventListener("keypress", addListAfterKeypress);
You can't get your tasks if you refreshed the page without storing them somewhere,
so I recommend you to use local storage as a starting for you
the whole idea here is when you press enter to submit a task you need to make an array to push each task and at the same time update your local storage with that array
and then you need to make an event listener ('DOMContentLoaded') so if you refresh your page then you retrieve tasks from local storage and append them to dom
I hope this answer clarify your issue
// select elements
const input = document.getElementById('userInput');
const ul = document.querySelector('ul');
function createListElement() {
var li = document.createElement("li");
li.appendChild(document.createTextNode(input.value));
ul.appendChild(li);
// update array with storage
let arr = getTasks();
// push li text to array
arr.push(li.textContent);
// update localStorage
localStorage.setItem('tasks', JSON.stringify(arr));
function crossOut() {
li.classList.toggle("done");
}
li.addEventListener("click", crossOut);
var dBtn = document.createElement("button");
dBtn.appendChild(document.createTextNode("X"));
li.appendChild(dBtn);
dBtn.addEventListener("click", deleteListItem);
function deleteListItem() {
li.classList.add("delete");
}
}
//enter works
function addListAfterKeypress(event) {
if (input.value.length > 0 && event.which === 13) {
createListElement();
// wipe out input
input.value = '';
}
}
input.addEventListener("keypress", addListAfterKeypress);
// check localStorage
function getTasks() {
let tasks;
if (localStorage.getItem('tasks') === null) {
tasks = [];
} else {
tasks = JSON.parse(localStorage.getItem('tasks'));
}
return tasks;
}
// on dom loading append tasks
window.addEventListener('DOMContentLoaded', () => {
// get tasks from storage
let arr = getTasks();
// loop through tasks
let foo = '';
arr.forEach(item => {
foo += `
<li>${item}<button>X</button></li>
`;
});
// append tasks to dom
ul.innerHTML = foo;
});
<div class="row">
<input id="userInput" type="text" placeholder="New item..." maxlength="190" autofocus>
<button id="enter">Add</button>
</div>
<div class="row" id="items">
<div class="listItems col-12">
<ul class="col-12 offset-0 col-sm-8 offset-sm-2">
</ul>
</div>
</div>
</div>
It depends on the use case, if it's something like managing date centrally you need to go with database. if it is some minor data user specific you can go with local storage.
To use localStorage in your web applications, there are five methods to choose from:
setItem(): Add key and value to localStorage
getItem(): Retrieve a value by the key from localStorage
removeItem(): Remove an item by key from localStorage
clear(): Clear all localStorage
key(): Passed a number to retrieve nth key of a localStorage
setItem()
window.localStorage.setItem('name', 'Obaseki Nosa');
const person = {
name: "Obaseki Nosa",
location: "Lagos",
}
window.localStorage.setItem('user', JSON.stringify(person));
getItem()
window.localStorage.getItem('user');
JSON.parse(window.localStorage.getItem('user'));
removeItem()
window.localStorage.removeItem('name');
clear()
window.localStorage.clear();
Note: localStorage is synchronous, with no data protection and limited to 5MB across all major browsers
I am using angular reactive form and making distance input fields which has two input boxes called From and To.
HTML:
<form [formGroup]="form">
<button (click)="addRow()">Add</button>
<div formArrayName="distance">
<div
*ngFor="let item of form.get('distance').controls; let i = index"
[formGroupName]="i"
style="display: flex"
>
<input type="number" placeholder="From" formControlName="from" />
<div><input type="number" placeholder="To" formControlName="to" /></div>
</div>
</div>
<br /><br />
<button type="submit" [disabled]="!form.valid">Submit</button>
</form>
TypeScript:
ngOnInit() {
this.form = this.fb.group({
distance: this.fb.array([]),
});
this.addRow()
}
addRow() {
const control = this.form.controls.distance as FormArray;
control.push(this.fb.group({
from: ['',Validators.required],
to: ['',Validators.required]
}));
}
Here you could able to see the two input boxes in default as from and to.
There is an add button at top and upon clicking add button the rows with same input fields gets added and forms as array.
Here i am in the need of restriction that user should not allowed to enter the previous row to value and also not the value lesser than that.
For eg.,
In the first row, if user enters the below values like 0 and 5 for from and to respectively,
"distance": [
{
"from": 0,
"to": 5
}
]
After clicking add and in second row in From input box user needs to be restricted on adding the values of 5 and lesser than that (which means those values were already entered).
So like this is invalid,
{
"distance": [
{
"from": 0,
"to": 5
},
{
"from": 5,
"to": 10
}
]
}
Here "from": 5, or "from": 4(or)3(or)2(or)1, anything is invalid in second row..
Only 6 and greater than 6 is valid.
Likewise for each row it needs to check for previous row to value and validation needs to be done.
Kindly help me to achieve this type of validation of restricting the user not to enter previous row to value (or) lesser than that in current row's from value.
Working Example: https://stackblitz.com/edit/disable-group-control-value-on-another-control-value-for-j58atx
Edit:
Tried with input change like,
<input type="number" (input)="onInputChange($event.target.value)" placeholder="From" formControlName="from">
in the link https://stackblitz.com/edit/disable-group-control-value-on-another-control-value-for-ymfpkj but not sure whether i am going correct..
Kindly change if this procedure is wrong.
Finally I decided divide the two conditions. see new stackblitz
ngOnInit() {
this.form = this.fb.group({
distance: this.fb.array([], this.distanceValidator()),
});
this.addRow()
}
addRow() {
const control = this.form.controls.distance as FormArray;
control.push(this.fb.group({
from: ['', Validators.required],
to: ['', Validators.required]
}, { validator: this.greaterValidator() }));
}
setDefault() {
const control = this.form.controls.distance as FormArray;
this.default.forEach(data => {
control.push(this.fb.group({
from: [data.from, Validators.required],
to: [data.to, Validators.required]
}, { validator: this.greaterValidator() }));
});
}
greaterValidator() {
return (fa: FormGroup) => {
return fa.value.to && fa.value.to < fa.value.from ? { error: "from greater than to" } : null;
}
}
distanceValidator() {
return (fa: FormArray) => {
let ok = true;
for (let i = 1; i < fa.value.length; i++) {
ok = (!fa.value[i].from || fa.value[i].from > fa.value[i - 1].to) && (!fa.value[i].to || fa.value[i].to > fa.value[i - 1].from);
if (!ok)
return { error: "from/to yet included", index: i }
}
return null
}
}
And the .html
<form [formGroup]="form">
<button (click)="addRow()">Add</button>
<div formArrayName="distance" >
<div
*ngFor="let item of form.get('distance').controls; let i = index"
[formGroupName]="i"
style="display: flex">
<input type="number"
placeholder="From"
formControlName="from">
<div>
<input type="number"
placeholder="To"
formControlName="to">
</div>
<span *ngIf="item.errors">*</span>
<span *ngIf="form.get('distance')?.errors && form.get('distance')?.errors.index==i">**</span>
</div>
</div>
<div *ngIf="form.get('distance')?.errors">{{form.get('distance')?.errors.error}}</div>
<br><br>
<button type="submit" [disabled]="!form.valid"> Submit </button>
</form>
<button (click)="setDefault()"> Set Default Values </button>
Update:Actually only when find an error not control more.
Moreover, if the from and to before is empty, don't give an error. For avoid this we can "convert" to number, writing
let ok = (!fa.value[i].from || fa.value[i].from > +fa.value[i - 1].to)
&& (!fa.value[i].to || fa.value[i].to > +fa.value[i - 1].from);
(see the "+" in +fa.value[i-1].to and +fa.value[i-1].from
Well, As we decided the error we send, imagine you has 6 rows and the line in position 0, in position 3 and in position 4 (0 is the first row) send a error like
{error:"there are errors",indexError:",0,3,4,"}
This allow inside the *ngFor write some like
<span *ngIf="form.get('distance')?.errors &&
form.get('distance')?.errors.indexError.indexOf(','+i+',')>=0">
**
</span>
Well, our distanceValidator becomes like
distanceValidator() {
return (fa: FormArray) => {
let indexError:string="";
for (let i = 1; i < fa.value.length; i++) {
let ok = (!fa.value[i].from || fa.value[i].from > +fa.value[i - 1].to) && (!fa.value[i].to || fa.value[i].to > +fa.value[i - 1].from);
if (!ok)
indexError+=','+i;
}
return indexError?{error:"there are errors",indexError:indexError+','}:null
}
Someone can think that it's better return an array of errors, but this not allowed as to know in a easy way the row with errors. some like errors.find(x=>x.id==i) not work because we can not use find in a interpolation.
It's true that only compare one row with the inmediaty before. It's possible to check over all before -using a for (let j=i-1;j>0;j++){ok=ok && ...}-, but I think it's not necesary and we must be stingy in code. Remember that the function distanceValidator are executed several times
See another stackblitz
Just use a customValidation (I choose the validation in the same component
ngOnInit() {
this.form = this.fb.group({
distance: this.fb.array([], this.distanceValidator()),
});
this.addRow()
}
distanceValidator() {
return (fa: FormArray) => {
let ok = true;
let ok2 = fa.value.length ? (!fa.value[0].to || !fa.value[0].from) || fa.value[0].to > fa.value[0].from : true;
if (!ok2)
return { error: "from greater than to" }
for (let i = 1; i < fa.value.length; i++) {
if (fa.value[i].from && fa.value[i].to )
{
ok = (fa.value[i].from > fa.value[i - 1].to || fa.value[i].to < fa.value[i - 1].from);
ok2 = (fa.value[i].to > fa.value[i].from);
if (!ok)
return { error: "from/to yet included" }
if (!ok2)
return { error: "from greater than to" }
}
}
return ok && ok2 ? null : !ok?{ error: "from yet included" }:{ error: "from greater than to" }
}
}
You can see the error like another
<div *ngIf="form.get('distance')?.errors">
{{form.get('distance')?.errors.error}}
</div>
see [stackblitz forked][1]
I'm trying to find a way through ngclass conditions to check all boxes automatically when the user deselect them both .
to make it clear , there're two item in cards as it shown in the image , when the user deselect first one , then the other card will be checked , then when the user deselect the second one , It should automatically select both of them again .
I tried the code below but it didn't give the desired result
HTML
<ion-list ng-show="transactionsCtrl.showCardBox">
<ion-item class="bg-blue p-0">
<div class="dotted-1"></div>
<div ng-repeat="singleCard in transactionsCtrl.cards">
<div class="row p-tb0 m-t5" ng-click="transactionsCtrl.ToggleCardFilter(singleCard.g_id)">
<div class="col col-20">
<div ng-class="{'image-size-1-50 white-checked-image': singleCard.selected || singleCard [0].selected, 'image-size-1-50 white-unchecked-image': !singleCard.selected}"></div>
</div>
<div class="col col-80">
<div class="sub-title-white oslight">{{(singleCard.cardNumberMasked).slice(-5)}} <span class="m-l10 right {{transactionsCtrl.PlaceCardImageClassForFilter(singleCard.g_productSubCategory)}}"> </span></div>
</div>
self.ToggleCardFilter = function(cardId) {
// toggle on-screen indicator
for (var c = 0; c < self.cards.length; c++)
if (self.cards[c].g_id == cardId)
self.cards[c].selected = !self.cards[c].selected;
// store card status to filter
var idx = $scope.transactionFilter.cards.indexOf(cardId);
if (idx == -1)
$scope.transactionFilter.cards.push(cardId);
else
$scope.transactionFilter.cards.splice(idx, 1);
self.applyFilterChange();
};
here's the function which display the result for the cards even if both of them aren't selected
DatabaseFactory.GetAllFromDB("Card").then(function(result) {
self.cards.length = 0;
if (result.rows.length > 0) {
var addAllCardsToFilter = false;
// first time and when all cards are deselected
if ($scope.transactionFilter.cards.length === 0)
addAllCardsToFilter = true;
for (var r = 0; r < result.rows.length; r++) {
self.cards.push(result.rows.item(r));
// initial fill and when all card were deselected - avoid empty display
if (addAllCardsToFilter)
$scope.transactionFilter.cards.push(self.cards[r].g_id);
if ($scope.transactionFilter.cards.indexOf(self.cards[r].g_id) > -1)
self.cards[r].selected = true;
}
}
});
Checked is a state so you cannot manage the state of the checkbox using ng-class. Please check my solution below:
I think this might help
HTML part
<div ng-app="myApp" ng-controller="CheckBoxController">
<input type="checkbox" ng-model="stateOption1" ng-change="changeTracker(stateOption1)">Option1
<input type="checkbox" ng-model="stateOption2" ng-change="changeTracker(stateOption2)">Option2
</div>
JavaScript Part
var myApp = angular.module('myApp',[]);
myApp.controller('CheckBoxController', ['$scope', function($scope) {
$scope.stateOption1 = true;
$scope.stateOption2 = true;
$scope.changeTracker = function(value)
{
if(!($scope.stateOption1 || $scope.stateOption2))
{
$scope.stateOption1 = true;
$scope.stateOption2 = true;
}
return value? false: true;
}
}]);
I am saving all data into localStorage. When a checkbox is checked function is called to change items state. It works fine. However after page refresh, last checked item gets unchecked (or if it was unchecked, it gets checked) while others are working just fine. Why does that 1 last action gets ignored after page is refreshed?
Here is codepen: http://codepen.io/kunokdev/pen/vGeEoY?editors=1010
(add few items and click on "click me" for all of them and then refresh page, last action will be ignored)
The view:
<div ng-app="TaskApp" ng-controller="ToDoCtrl">
<form>
<input type="text" ng-model="toDoItem">
<input type="submit" ng-click="addToDoItem()">
</form>
<div>
<ul>
<div
ng-repeat="item in toDoItems |
orderBy: 'createdAt'
track by item.createdAt">
<b>Content:</b> {{item.content}} <br>
<b>Completed?</b> {{item.completed}}
<md-checkbox ng-model="item.completed" ng-click="toggleToDoItem(item.completed)" aria-label="todo-checkbox">
CLICK ME
</md-checkbox>
</div>
</ul>
</div>
</div>
And JS:
var ls = {};
ls.get = function(key) {
return JSON.parse(localStorage.getItem(key));
};
// sets or updates a value for a key
ls.set = function(key, val) {
localStorage.setItem(key, JSON.stringify(val));
};
// returns true if value is set, else false
ls.isSet = function(key) {
var val = ls.get(key);
return ( null === val || 'undefined' === typeof val) ? false : true;
};
// removes a set item
ls.remove = function(key) {
localStorage.removeItem(key)
};
var TaskApp = angular.module('TaskApp', [
'ngMaterial',
'taskAppControllers'
]);
var taskAppControllers = angular.module('taskAppControllers',[]);
taskAppControllers.controller('ToDoCtrl', ['$scope',
function($scope){
//
loadToDoItems = function(){
var data = ls.get("toDoData");
if (data == null) data = [];
return data;
};
//
$scope.toDoItems = loadToDoItems();
//
$scope.addToDoItem = function(){
var toDoItems = $scope.toDoItems;
var newToDoItem = {
"content" : $scope.toDoItem,
"createdAt" : Date.now(),
"completed" : false
}
toDoItems.push(newToDoItem);
ls.set("toDoData", toDoItems);
$scope.toDoItem = "";
};
//
$scope.toggleToDoItem = function(item){
console.log('test');
var toDoItems = $scope.toDoItems;
for (var i = 0; i < toDoItems.length; i++)
if (toDoItems[i].createdAt === item){
if (toDoItems[i].completed == true)
toDoItems[i].completed = false;
else
toDoItems[i].completed = true;
}
ls.set('toDoData', toDoItems);
};
//
}]);
md-checkbox is designed to toggle whatever you put in ng-model so with your code, md-checkbox was toggling the completed property and then you were changing it back again in your $scope.toggleToDoItem function. Why this worked for all the items except the last clicked I am unsure.
So I changed the ng-click to only save the items to local storage and still got the same problem which leads to me believe the problem is caused by using ng-click on an md-checkbox.
<md-checkbox ng-model="item.completed" ng-click="saveToLocalStorage()" aria-label="todo-checkbox">
CLICK ME
</md-checkbox>
$scope.saveToLocalStorage = function() {
ls.set('toDoData', $scope.toDoItems);
};
So I removed the ng-click and set up a watch on $scope.toDoItems.
<md-checkbox ng-model="item.completed" aria-label="todo-checkbox">
$scope.$watch("toDoItems", function() {
ls.set("toDoData", $scope.toDoItems);
}, true);
Codepen
-- EDIT --
Just read the documentation and feel like an idiot, you should use ng-change instead of ng-click. From the docs regarding ng-change:
Angular expression to be executed when input changes due to user interaction with the input element.
That being said, the above about not needing to toggle the completed property yourself still stands.
You are passing item.completed (in the HTML) to your toggleToDoItem(item) method. In your array loop, you then compare the item.Created field to the item.completed parameter. This is comparing a Date type to a Bool. How is that supposed to work?
I have 3 sections of input fields separated with different heading(Laser Pass, The Giggsy, The set up) generated from a JSON array. Here is what it looks like:
I want to compare two fields Score and Attempts and show an error message if the value of Score is larger then Attempts. Something like this:
But some section like, The Giggsy have a different type of input fields and no need to compare/check those fields. Only where it has SCORE and ATTEMPTS should compare.
When the section is filled up Show success message like this:
What I can do to make those things in angular way. Here is what I've done so far: PLUNKER
HTML:
<div class="row" ng-repeat="all in options">
<h4> {{ all.name}} </h4>
<div class="col-sm-5ths" ng-repeat="measurement in all.measurements">
<div class="form-group no-margin form-oneline">
<label style="width: 100%">{{ measurement.name }}</label>
<input ng-model="measurement.value" type="{{ measurement.type }}" min="{{ measurement.min }}" max="{{ measurement.max }}" class="form-control display-inline" required>
<label style="width: 100%">{{ measurement.scale }}</label>
</div>
</div>
<span style="color:red;" ng-show="testDataFieldWarning(options.measurements)">
Score can't be larger then Attempts
</span>
<span style="color:Green;" >
Done!!
</span>
</div>
<button type="submit" style="margin-top:50px;" ng-disable="">Submit</button>
JS
$scope.testDataFieldWarning = function (measurements) {
var score = 0 , attempts = 0;
angular.forEach(measurements, function(measurement) {
if((measurement.name) == 'Score'){
score = measurement.value;
}
if((measurement.name) == 'Attempts'){
attempts = measurement.value;
}
});
return attempts < score;
}
$scope.testDataFieldValidate = function (measurement) {
var isInvalid = false;
angular.forEach(measurement, function(v) {
if(typeof (v.value) == 'undefined'){
isInvalid = true;
}
});
return (isInvalid);
}
Sorry for bad English and explanation.
I forked your plunker and added some additional validating functions...
function isScoreField(measurements) {
if (measurements[1].name === 'Score' && measurements[2].name ==='Attempts') {
return true;
} else {
return false;
}
}
$scope.testDataFieldInvalid = function (measurements) {
if (isScoreField(measurements) && parseInt(measurements[2].value) < parseInt(measurements[1].value)) {
return true;
} else {
return false;
}
};
$scope.testDataFieldsEntered = function (measurements) {
if (measurements[1].value && measurements[2].value) {
return true;
} else {
return false;
}
};
... that will conditionally show/hide the done/error messages.
<span style="color:red;" ng-show="testDataFieldInvalid(all.measurements)">
Score can't be larger than Attempts
</span>
<span style="color:Green;" ng-show="testDataFieldsEntered(all.measurements) && !testDataFieldInvalid(all.measurements)">
Done!!
</span>
Hope this helps!