Why is my todo functions not working? - javascript

I am using angular.js and ionic framework to make a todo app. My remove todoFunction works well but my add new todo function does not work. Once I click on the Add Item button, no text appears on the todo list though new space apears to be added for my new todo item just created.
The second time I try to create a new todo item, no space is added, andn nothing works.
Here is my toController:
facebookExample.controller('todoController', ['$scope', function($scope) {
// Initialize the todo list array
//if local storage is null save the todolist to local storage
if (localStorage.getItem("mytodos") === null)
{
localStorage.setItem("mytodos", angular.toJson($scope.todoList));
}else
{
//set the todolist from local storage
$scope.todoList = angular.fromJson(localStorage.getItem("mytodos"));
}
// Add an item function
$scope.todoAdd = function() {
//check to see if text has been entered, if not exit
if ($scope.todoInput === null || $scope.todoInput === ''){return;}
//if there is text add it to the array
$scope.todoList.push({todoText:$scope.todoInput, done:false});
//clear the textbox
$scope.todoInput = "";
//resave the list to localstorage
localStorage.setItem("mytodos", angular.toJson($scope.todoList));
};
$scope.remove = function() {
//copy list
var oldList = $scope.todoList;
//clear list
$scope.todoList = [];
//cycle through list
angular.forEach(oldList, function(x) {
//add any non-done items to todo list
if (!x.done) $scope.todoList.push(x);
});
//update local storage
localStorage.setItem("mytodos", angular.toJson($scope.todoList));
};
//The Update function
//This waits 100ms to store the data in local storage
$scope.update = function() {
//update local storage 100 ms after the checkbox is clicked to allow it to process
setTimeout(function(){
localStorage.setItem("mytodos", angular.toJson($scope.todoList));
},100);
};
}]);
And here is my view template:
<ion-view title="Tasks" ng-controller="todoController">
<ion-content>
<!-- our list and list items -->
<h1>Tasks</h1>
<div class="item item-input-inset">
<label class="item-input-wrapper">
<!-- The actual input tag which is bound to the todoInput using ng-model -->
<input type="text" placeholder="Add New Item" ng-model="todoInput" size="100">
</label>
<!-- Our button thay will call our funtion to add a new todo item -->
<button class="button button-small" ng-click="todoAdd()">
Add Item
</button>
</div>
<div ng-repeat="x in todoList">
<li class="item item-checkbox">
<label class="checkbox">
<!-- this is the checkbox element, you will see it is bound to the done setting in the items array -->
<!-- When clicked it calls the update function to update the item to its done status -->
<input type="checkbox" ng-model="x.done" ng-click="update()">
</label>
<!-- this is a span tag that shows the item text, I am using ng-bind, instead of the span tag we could have used {{x.todoText}} as well -->
<span>{{x.todoText}} </span>
</li>
</div>
<!-- the remove button will call the remove function and remoave all items that are marked done -->
<button class="button button-block button-assertive" ng-click="remove()">
Remove Checked Items
</button>
</ion-content>
</ion-view>

You are pushing onto a non-existent variable, $scope.todoList does not yet exist.
You need to define $scope.todoList as an array first, then you can save or load from local storage. Now the first time you run the app when there is no list saved in local storage, a new array will be created.
$scope.todoList = [];
if (localStorage.getItem("mytodos") === null) {
localStorage.setItem("mytodos", angular.toJson($scope.todoList));
} else {
$scope.todoList = angular.fromJson(localStorage.getItem("mytodos"));
}
Because of this problem, you may have accidentally saved bad data to local storage, so if you still have issues just save an empty array to local storage. Otherwise your app will work fine, have a look: JSFiddle

Related

How to maintain a constant scope in angular?

I have an angular service that fetch contacts data from the database, I then use these data so that the user can add one contact or more to a deal, so there is a many to many relationship between the deal and contact, the selected data are displayed in a grid (syncfusion grid).
I need to have a constant list of the data retrieved from database, and a varible containing the selected contacts that I pass to the syncfusion grid, when the user add from the dropdown I add this contact to the grid and remove it from the dropdown list, and if I deleted it from the grid I return it back to the drop down list, so here's my code:
this service get the contact list:
var contactsListDB = [];
contactService.getAll().then(function (contacts) {
contactsListDB = contacts.data;
$scope.contactsList = contactsListDB; // the scope used in the select input in the HTML
});
the method that add contact:
$scope.addContact = function (contact) {
var contactJson = JSON.parse(contact);
$scope.dealContacts.push(contactJson);
var index = $scope.contactsList.findIndex(x => x.id == contactJson.id);
SyncFusionDealContact($scope.dealContacts);
$scope.contactsList.splice(index, 1);
}
this function is invoked in HTML:
<ng-form name="ContactForm">
<md-input-container>
<label>{{"Contact" | translate}}</label>
<md-select ng-model="contact">
<md-option value="{{null}}">-- {{"selectContact" | translate}} --</md-option>
<md-option ng-repeat="contact in contactsList" value="{{contact}}">{{contact.name}}</md-option>
</md-select>
</md-input-container>
<md-input-container>
<div>
<button class="btn btn-primary" type="submit" ng-disabled="!ContactForm.$valid" ng-click="addContact(contact)" aria-label="submit">{{'add' | translate}}</button>
</div>
</md-input-container>
<div id="GridDealContacts">
<script id="columnTemplateContacts" type="text/x-jsrender">
<a class="btn btn-danger btn-xs contactDelete" data-id="{{:id}}" ng-click="DeleteContact{{:id}}"><i class="fa fa-trash-o "></i></a>
<!--add delete button-->
</script>
</div>
</ng-form>
When the page is loaded I check if the object is being edited and then I exclude the existing contacts from the list comming from contacts table:
$scope.dealContacts = deal.contacts;
SyncFusionDealContact($scope.dealContacts);
execludeContacts()
execludeContacts function:
function execludeContacts() {
var exIds = [];
if ($scope.dealContacts.length > 0) {
exIds = $scope.dealContacts.map(x=> x.id)
}
var conts = contactsListDB;
conts.forEach(function (item, index) {
if (exIds.includes(item.id)) {
conts.splice(index, 1);
}
})
$scope.contactsList = conts;
}
this function handles delete action:
$scope.DeleteContact = function (id, index) {
if (id <= 0) {
$scope.dealContacts.splice(index, 1)
SyncFusionDealContact($scope.dealContacts);
}
else {
if (confirm("Are You Sure?")) {
dealService.deleteContact(id, $routeParams.id).then(function (success) {
if (success.data.isSuccess) {
SyncFusionDealContact($scope.dealContacts);
var one = contactsListDB.filter(x => x.id == id)[0];
$scope.contactsList.push(one);
$scope.dealContacts.splice(index, 1);
}
else {
alert('Cannot delete');
}
SyncFusionDealContact($scope.dealContacts);
});
}
}
}
In the code above I tried to save a copy of the contacts list in a variable that can't be changed contactsListDB, so that when a record is deleted from the grid I can get it back from this array to add it in the drop-down list again, but what happens is that the array is changed:
Screenshot:
I'm not sure of what you're asking, but I think you want to 'save' the data somewhere? If that's the case, take a look at using a factory or service. I prefer using a factory because of its simplicity.
From the short period I've worked with Angular, I know that a factory will always stay alive (even while switching views in SPA) until you refresh the page.
In a simple example, the factory will hold a list of the contacts, and have several functions like your addContact and deleteContact. This makes your code less cluttered and you just have to call the factory and its functions in your code.
I currently can't give a specific example (as I prefer using my own factories and its usage), but here you can find a comparison between factory and service and how to use them.
If I misunderstood your question, please do tell. :D

Retrieving an array of objects from localstorage in angularjs

I am new to Ionic and AngularJS. I am trying to store an array of objects to local storage. The code below works fine if I only store one object (I am able to retrieve the data). The problem arises when I try to store and access data from an array of objects (prints blank).
Here is my index.html
<ul class="list">
<li class="item" ng-repeat="alarm in alarms">
{{alarm.hour}}: {{alarm.min}} {{alarm.pos}}
<span class="item-toggle">
<label class="toggle toggle-balanced">
<input type="checkbox" ng-model="alarm.on" ng-click="completeTask($index)">
<div class="track">
<div class="handle"></div>
</div>
</label>
</span>
</li>
</ul>
</ion-content>
controller.js
$scope.createalarm = function (alarm) {
$scope.alarms.push({
hour : alarm.hour , min : alarm.min , pos : alarm.pos , on : true
});
window.localStorage['alarms'] = JSON.stringify($scope.alarms);
$scope.setalarm.hide();
};
$scope.getalarms = function (){
$scope.alarms = JSON.parse(window.localStorage['alarms'] || '[]');
};
I validate data stored in local storage using Storage Inspector in Mozilla. This is the result:
Can anyone Help me?
You can use this code to store an array object to local storage:
localStorage.setItem('alarms', JSON.stringify($scope.alarms));
to retrieve the stored values use this:
$scope.alarms = (localStorage.getItem('alarms')!==null) ? JSON.parse(localStorage.getItem('alarms')) : [];
localStorage.getItem('itemName') it's what you are looking for, you need to modify your getalarms function:
$scope.getalarms = function (){
$scope.alarms = JSON.parse(localStorage.getItem('alarms'));
};
The previously set alarms must first retrieved and stored in a variable. Then push the new alarm in this variable and put them back in storage. With this way you can store an array of objects in the storage. If you dont retrieve the currently saved alarms then you will not be able to save more than one alarm in the storage because every new one will overwrite the previous
$scope.createalarm = function (alarm) {
//retrive all alarms that are currently in our localStorage
//if we dont do that every new alarm will overwrite the old one
$scope.alarms = JSON.parse(window.localStorage.getItem('alarms')) || [];
//push the new created alarm
$scope.alarms.push({
hour : alarm.hour , min : alarm.min , pos : alarm.pos , on : true
});
//save the newly created alarm back in the storage
window.localStorage.setItem('alarms', JSON.stringify($scope.alarms));
$scope.setalarm.hide();
$scope.getalarms = function (){
$scope.alarms = JSON.parse(window.localStorage.getItem('alarms'));
};
}

AngularJs - ngRepeat Delete a particular line in View

I have a simple rendering of 3 items with their particular ids glued with there name fields. I want to show a particular message whenever I delete that line only with the button to follow. When I am doing it, I have problem in passing the id for delete for that particular line only, and all the n rows (here 3) are getting deleted.
HTML :
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<div>
<p ng-repeat="x in List" ng-show="!deleted">{{x.name}}<button ng-click="del(x.id)">Delete</button></p>
<p ng-show="deleted">This line has been deleted.</p>
</div>
<script>
//module declaration
var app = angular.module('myApp',[]);
//Controller declaration
app.controller('myCtrl',function($scope){
$scope.deleted = false;
$scope.List = [{"name":"Peter Martin","id":"1"},{"name":"Lita Schedan","id":"2"},{"name":"Jenni Markints","id":"3"}];
$scope.del = function(id){
//for this id -> only
$scope.deleted = true;
}
});
</script>
</body>
</html>
This means, if I put delete button of line 2. The first and second rows must come with the delete button, but in the middle line the message "This line has been deleted" must show.
You should basically show the row which has deleted flag false by using angular filter. For that you need to make sure you List object show have deleted prop to false on initial load. Then on click pass whole row object to del method. And for showing error message you could use $timeout that will show an message for half second
Markup
<div>
<p ng-repeat="x in List | filter : {deleted: false}">
{{x.name}}
<button ng-click="del(x)">Delete</button>
</p>
<p ng-show="deleted">Line with id {{deletedId}} has been deleted.</p>
</div>
And on click of element make delete flag to true
Controller
//Controller declaration
app.controller('myCtrl',function($scope, $timeout){
$scope.deleted = false;
$scope.List = [{"name":"Peter Martin","id":"1"},{"name":"Lita Schedan","id":"2"},{"name":"Jenni Markints","id":"3"}];
angular.forEach($scope.List, function(ele){
ele.deleted = false;
})
$scope.del = function(x){ //passed object
x.deleted = true;
//for showing message
$scope.deleted = true;
$scope.deletedId = x.id;
$timeout(function(){
$scope.deleted = false;
$scope.deletedId = undefined;
}, 500)
}
});
If you really wanted to remove element from the array you could use .slice() method by passing $index to it

How do I make sure that each instance of my angular model has certain characteristics?

I have some tasks to do . I want to make sure that each task has a maximumFundPossible property of $1000 constant, a currentfund property that is variable as it will be updated as tasks are being funded , a budget property that is variable but constant for each task item . That task item property has to be set to a certain amount when being created. I guess I will have to add an input field for that amount to be set during task creation ? How ?
I already have my todo tasks but I am stuck as I am a few days new to angular and I hope to fully learn it through this project.
How do I modify my code so that my todo tasks have the described above properties? Also, when I click do button or do for free button, etc, will my current views keep track of that specific task X ? How do I keep track of that task X so that my do , doforfree or any other function I will be using affects only that specific task X.
I know it s too much to ask but angularjs just has a high learning curve.
Thanks a lot for your help
Here is my todoController:
facebookExample.controller('todoController', ['$scope', function($scope) {
// Initialize the todo list array
//if local storage is null save the todolist to local storage
$scope.todoList = [];
if (localStorage.getItem("mytodos") === null)
{
localStorage.setItem("mytodos", angular.toJson($scope.todoList));
}else
{
//set the todolist from local storage
$scope.todoList = angular.fromJson(localStorage.getItem("mytodos"));
}
// Add an item function
$scope.todoAdd = function() {
//check to see if text has been entered, if not exit
if ($scope.todoInput === null || $scope.todoInput === ''){return;}
//if there is text add it to the array
$scope.todoList.push({todoText:$scope.todoInput, done:false});
//clear the textbox
$scope.todoInput = "";
//resave the list to localstorage
localStorage.setItem("mytodos", angular.toJson($scope.todoList));
};
// Each task is limited to limit total funding = $1000
//current funding ; max funding; ballance funding = $1000-current
// Variables or columns for taskX
/****************/
// Task Counter for Task X
// If task-assignment is aproved by admin
// Set Task Counter to D-63
// Send reminder to Person assigned for task X in Due Date
// Do for money button is diabled unless task is funded
// Do for free is always enabled unless task is already assigned
/**************/
//Updating state of task to done only on due date
// if Dtime > DueDate
// UserX can update task to done
// Admin is notified by email of the action
// Admin should aprove of disaprove within 3 days
// Admin can aprove to Done or Undone
/*******************/
// Admin aproves of Task X to done
// Task X is updated to state of done
//if for moeny and amount raised for task >
// Payment is initiated to Tasker if task was funded and done for money
// Payment initiated from Company to Admin if task was done for free
//Do button
$scope.do = function(){
// If current user clicks do,
// Assign the current user to the task with a state of pending admin aproval
// Counter variable for dute date is initiated with D-63
};
$scope.doForFree = function(){
// Task is assigned to user with a state of pending-aproval-of-admin- assignment-do-for-free
// Admin is notified of do-for-free-request
};
// if admin aproves user that requested to do task X for money
// task X is assigned to that user
// state of task is assigned to admin-aproved-assignment-for-money
// the User is notified of admin aproval
// Task due date is created/updated
// if admin aproves user that requested to do task X for free
// task X is assigned to that user
// state of task is assigned to admin-aproved-assignment-for-free
// the User is notified of admin aproval
// Task due date is created/updated
//fund button
$scope.fund = function(){
//Redirect current user to paypal for payment to You-Serve
// Maximum payment cannot exceed Maximum amount - what 's already funded
// Need to keep track of already funded amount
// Need to keep track of task cost/price
// If paypal payment was done successfully
// Update already funded amount
// Update maximum amount for extra funding
// update the fully funded variable/boolean
// Send task/user/amount to database
// If payment fails, take out state of being funded
};
$scope.remove = function() {
//copy list
var oldList = $scope.todoList;
//clear list
$scope.todoList = [];
//cycle through list
angular.forEach(oldList, function(x) {
//add any non-done items to todo list
if (!x.done) $scope.todoList.push(x);
});
//update local storage
localStorage.setItem("mytodos", angular.toJson($scope.todoList));
};
//The Update function
//This waits 100ms to store the data in local storage
$scope.update = function() {
//update local storage 100 ms after the checkbox is clicked to allow it to process
setTimeout(function(){
localStorage.setItem("mytodos", angular.toJson($scope.todoList));
},100);
};
}]);
And here is my view:
<div ng-app="facebookExample" view-title="Tasks">
<div ng-controller="todoController">
<h1>Tasks</h1>
<div class="item item-input-inset">
<label class="item-input-wrapper">
<!-- The actual input tag which is bound to the todoInput using ng-model -->
<input type="text" placeholder="Add New Item" ng-model="todoInput" size="100">
</label>
<!-- Our button thay will call our funtion to add a new todo item -->
<button class="button button-small" ng-click="todoAdd()">
Add Task
</button>
</div>
<div ng-repeat="x in todoList">
<li class="item item-checkbox">
<label class="checkbox">
</label>
<!-- this is the checkbox element, you will see it is bound to the done setting in the items array -->
<!-- When clicked it calls the update function to update the item to its done status -->
<input type="checkbox" ng-model="x.done" ng-click="update()"/>
<!-- this is a span tag that shows the item text, I am using ng-bind, instead of the span tag we could have used {{x.todoText}} as well -->
<button class="fund-button" style= "float: left;" ng-click="fund()">Fund</button>
<span>{{x.todoText}}</span>
<button class="doButton" style= "float: right; margin-right: 2px;" ng-click="do()">Do</button>
</li>
</div>
<!-- the remove button will call the remove function and remoave all items that are marked done -->
<button class="button button-block button-assertive" ng-click="remove()">Remove Checked Tasks
</button>
</div>
</div>:
here is a simple of example of passing an item from ng-repeat to your actions:
<div ng-controller="MyCtrl">
<input type="text" ng-model="newMessage">
<button ng-click="addMessage(newMessage)">
Add
</button>
<ul>
<li ng-repeat="message in messages">
{{message}}
<button ng-click="remove(message, $index)">remove</button>
<button ng-click="uppercase(message, $index)">uppercase</button>
<button ng-click="lowercase(message, $index)">lowercase</button>
</li>
</ul>
</div>
controller:
function MyCtrl($scope) {
$scope.newMessage = '';
$scope.messages = [];
$scope.addMessage = function(msg) {
$scope.messages.push(msg);
};
$scope.remove = function(msg, index) {
$scope.messages.splice(index, 1);
};
$scope.uppercase = function(msg, index) {
$scope.messages[index] = msg.toUpperCase();
};
$scope.lowercase = function(msg, index) {
$scope.messages[index] = msg.toLowerCase();
};
}
take a look at the jsfiddle and really understand how data is being referenced by your actions. for example, I am using the implicit $index variable to access the array of messages.
http://jsfiddle.net/heavyhorse/x4b2w84c/

Update unrelated field when clicking Angular checkbox

I have a list of checkboxes for people, and I need to trigger an event that will display information about each person selected in another area of the view. I am getting the event to run in my controller and updating the array of staff information. However, the view is not updated with this information. I think this is probably some kind of scope issue, but cannot find anything that works. I have tried adding a $watch, my code seems to think that is already running. I have also tried adding a directive, but nothing in there seems to make this work any better. I am very, very new to Angular and do not know where to look for help on this.
My view includes the following:
<div data-ng-controller="staffController as staffCtrl" id="providerList" class="scrollDiv">
<fieldset>
<p data-ng-repeat="person in staffCtrl.persons">
<input type="checkbox" name="selectedPersons" value="{{ physician.StaffNumber }}" data-ng-model="person.isSelected"
data-ng-checked="isSelected(person.StaffNumber)" data-ng-change="staffCtrl.toggleSelection(person.StaffNumber)" />
{{ person.LastName }}, {{ person.FirstName }}<br />
</p>
</fieldset>
</div>
<div data-ng-controller="staffController as staffCtrl">
# of items: <span data-ng-bind="staffCtrl.infoList.length"></span>
<ul>
<li data-ng-repeat="info in staffCtrl.infoList">
<span data-ng-bind="info.staffInfoItem1"></span>
</li>
</ul>
</div>
My controller includes the following:
function getStaffInfo(staffId, date) {
staffService.getStaffInfoById(staffId)
.then(success)
.catch(failed);
function success(data) {
if (!self.infoList.length > 0) {
self.infoList = [];
}
var staffItems = { staffId: staffNumber, info: data };
self.infoList.push(staffItems);
}
function failed(err) {
self.errorMessage = err;
}
}
self.toggleSelection = function toggleSelection(staffId) {
var idx = self.selectedStaff.indexOf(staffId);
// is currently selected
if (idx >= 0) {
self.selectedStaff.splice(idx, 1);
removeInfoForStaff(staffId);
} else {
self.selectedStaff.push(staffId);
getStaffInfo(staffId);
}
};
Thanks in advance!!
In the code you posted, there are two main problems. One in the template, and one in the controller logic.
Your template is the following :
<div data-ng-controller="staffController as staffCtrl" id="providerList" class="scrollDiv">
<!-- ngRepeat where you select the persons -->
</div>
<div data-ng-controller="staffController as staffCtrl">
<!-- ngRepeat where you show persons info -->
</div>
Here, you declared twice the controller, therefore, you have two instances of it. When you select the persons, you are storing the info in the data structures of the first instance. But the part of the view that displays the infos is working with other instances of the data structures, that are undefined or empty. The controller should be declared on a parent element of the two divs.
The second mistake is the following :
if (!self.infoList.length > 0) {
self.infoList = [];
}
You probably meant :
if (!self.infoList) {
self.infoList = [];
}
which could be rewrited as :
self.infoList = self.infoList || [];

Categories