How to maintain a constant scope in angular? - javascript

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

Related

Storing strings in array as user selects checkbox

I just started using angularJS and am trying to learn how to insert strings in array when user clicks checkbox and later access these values.
This is what I have so far:
<md-button class="toolbar-button" ng-click="removePlan()">
<md-icon>delete</md-icon>
</md-button>
<md-list-item ng-repeat="plan in plans">
<md-checkbox ng-model="plansSelected[plan._id]"> </md-checkbox>
</md-list-item>
apps.js
$scope.plansSelected = []
$scope.removePlan = function () {
for (var plan in $scope.plansSelected) {
if ($scope.plansSelected[plan] === true) {
projectResource.removePlan(plan).then(function (data) {
if (data.success) {
$scope.plans.push(data.plan)
}
})
}
}
}
projectResource.js
resource.removePlan = function (planId) {
return $http.delete(getPlanUrl(planId)).then(processResponse, processError)
}
function getPlansUrl () {
return getProjectUrl() + '/plans'
}
function getPlanUrl () {
return getPlansUrl() + '/' + $route.current.params.planId
}
In debugger this is how my array looks like:
$scope.plansSelected: Array[0]
57bd40128a58022800887d80:false
57bd40328a58022800887d88:false
57cc2bcbb452140500b0334a:false
I am checking if user removed selection from the checkbox and if the value is true then take that plan._id and pass in the loop to removePlan function.
Does anyone see what I am doing wrong, because this is not working.
Please be more specific what "this is not working" means - it's hard for us to detect that without being able to run it.
I can see that your use of $scope.plansSelected as an empty array is wrong. I would dispense with that and do it like this:
<md-list-item ng-repeat="plan in plans track by $index">
{{plan.name}} <md-checkbox ng-model="plans[$index].selected"> </md-checkbox>
</md-list-item>
This basically adds a 'selected' attribute to your plans array, and make it easier for you to iterate over the plans array and pull out the ones you want.

How to push data into array inside a pop up (pop up opens for particular id) using angularjs

I successfully added data to array using push method normally but failed to do so inside a pop up which opens up for a particular Id .
Here is my code:
<div class="form-group">
<label class="col-sm-3" for="pwd">Speciality:</label>
<div class="col-sm-4">
<input type="text" class="form-control" ng-model="spec" id="usr">
<button type="submit" ng-click="addSpeciality()">Add </button>
</div>
<div class="col-sm-5">
<ul>
<li ng-repeat="spec in speciality">
{{ spec }}
<button ng-click="removeSpeciality($index)">Remove</button>
</li>
</ul>
</div>
</div>
Controller code:
$scope.speciality=[];
$scope.addSpeciality = function(){
$scope.speciality.push($scope.spec);
$scope.spec = '';
};
$scope.removeSpeciality = function(index) {
$scope.speciality.splice(index, 1);
};
This is point when you need a Factory. Forget about storing any data in controllers. Really - FORGET! The only proper way to share data across application is to define Factory you like and inject it separately in every place you gonna work with that data.
You should not store data in $scope. $scope itself is an instance bind to DOM element. So only way you access any data from any level of deepness is using $parent what is a great mistake.
Your controller should $inject that Factory and call related methods when you need to change anything in data. Never pass business logic in controllers. They are like Strategy design pattern - place where you only tell what business logic should take place when you got trigger (click for example):
SpecialityFactory.addSpeciality = function () {
SpecialityFactory.specialities.push({});
}
SpecialityFactory.removeSpeciality = function (idx) {
SpecialityFactory.specialities.splice(idx, 1);
}
So it will be much easier to share business logic across your controllers:
// PageController
PageController.prototype.addSpeciality = function () {
SpecialityFactory.addSpeciality();
};
PageController.$inject = ['SpecialityFactory'];
// ModalController
ModalController.prototype.removeSpeciality = function (idx) {
SpecialityFactory.removeSpeciality(idx);
};
ModalController.$inject = ['SpecialityFactory'];
Well, first of all #Apperion is right, read his answer
but to get your code working try to add the controller to your wrapper HTML element
<div class="form-group" ng-controller="ExampleController">
and
with this array your ng-repeat won't work this way, you need to use this way:
<li ng-repeat="spec in speciality track by $index">

Why is my todo functions not working?

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

AngularJS Filter Best Practice

I have a filter I need to use two times (two pages/controllers), filtering on an object category. I can get this to work using this in each controller:
$scope.$on('category', function (event, arg) {
$scope.catSort = arg;
});
$scope.filterCat = function (item) {
return !$scope.catSort ?
item : (item.category == $scope.catSort);
};
The html i am filtering on:
<li dnd-draggable="item" ng-repeat="item in items| filter: filterCat">
</li>
I set the $scope.catSort which is what to filter on in a list of buttons the user can click:
<button class="btn btn-default" ng-click="$emit('category', 'all')">Show All</button>
<button class="btn btn-default" ng-click="$emit('category', 'Common')">Common</button>
The problem is, i have another html page with a different subset of categories that I need to filter on. Instead of just copying and pasting the $scope.filterCat function to filter again, I want to create a separate filter to inject into each controller (Thats best practice right?).
So what i have started is this for a separate filter:
angular.module("category-filter", [])
.filter('filterCat', function () {
return function (category, item) {
return !category ?
product : (item.category == $scope.catSort);
}
});
You can see i'm trying to get that category option as well. How do i reach into the scope and get the category? Should i use a service and on one of the button clicks set the category to filter on?
I'm still learning filters and what want to make sure its reusable and following best practices
Pass an object to the filter expression: filter:{category: '...', compareTo: '...'} and check it inside the filter function.

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