Ng-repeat - "Are you sure to delete ?" from a modal - angularjs

I'm retrieving a list of objects (item) from a Django API.
my_app.factory('list_of_items', function($resource) {
return $resource(
'/api/petdata/') });
Then I display everything in a html page within a ng-repeat:
<div ng-controller="ModalDemoCtrl">
<div ng-repeat="item in items | filter:{display:'1'} | orderBy: 'item_name'">
<div class="box box-widget widget-user">
{{ item.pet_name }}{% endverbatim %}
<button type="button" class="btn btn-box-tool" ng-click="askDelete(item)" href="#"><i class="fa fa-times"></i></button>
</div>
<div>
Everything's fine so far.
Then I want the user to be able to delete one of the item by clicking on the button from the html page.
What means deleting here :
1. Update the API database by changing the property "display:1" to "display:0".
2. Remove the item from the ng-repeat.
I want to make a "Are you sure" modal to confirm the delete process.
This is the askDelete function.
angular.module('djangular-demo').controller('Ctrl_List_Of_Pets', function($scope, $http, $window,$filter,list_of_pets,pet_by_id,$uibModal) {
$scope.items = list_of_items.query()
$scope.askDelete = function (idx,item,size,parentSelector) {
// console.log("PET",$scope.pet_to_be_undisplayed);
var parentElem = parentSelector ?
angular.element($document[0].querySelector('.modal-demo ' + parentSelector)) : undefined;
var modalInstance = $uibModal.open({
animation: true,
ariaLabelledBy: 'LOL',
ariaDescribedBy: 'modal-body',
templateUrl: "myModalContent.html",
controller: function($scope) {
$scope.ok = function() {
modalInstance.close();
};
$scope.cancel = function() {
modalInstance.dismiss('cancel');
};
},
size: size,
appendTo: parentElem,
resolve: {
}
});
modalInstance.result.then(function() {
reallyDelete(item);
});
};
var reallyDelete = function(item) {
$scope.entry = items_by_id.get({ id: item.id }, function() {
// $scope.entry is fetched from server and is an instance of Entry
$scope.entry.display = 0;
$scope.entry.$update({id: $scope.entry.id},function() {
//updated in the backend
});
});
$scope.items = window._.remove($scope.items, function(elem) {
return elem != item;
});
};
});
What works :
Updating the DB works with a PUT request (code hasn't been provided).
What doesn't work :
Removing the item from the ng-repeat never works. Or it throws me an error like here because it doesn't know window._.remove or it doesn't know $scope.items. It depends from what I try. Or the modal close and there is no update of the ng-repeat list, no refresh and every items remain whereas the PUT request to update worked.
I read every article on scope inheritance and I think I didn't make any mistake here but I'm might be wrong. I've been struggling for too long so I post here !
Would you suggest anything to make it work ?
Thank you for your rime.

First:
$scope.askDelete = function (idx,item,size,parentSelector) receives the item index, the item, size, and parent selector... and you are calling ng-click="askDelete(item)"
I assume you are attempting to pass the item, but in askDelete you are receiving as first parameter the index (maybe you should do ng-click="askDelete($index)"?)
Second:
In reallyDelete why are you removing the items array like this:
$scope.items = window._.remove($scope.items, function(elem) {
return elem != item;
});
?
IMHO, it would be a much cleaner code if we just do:
$scope.items.splice(idx, 1) //<- idx would be the idx of the entry in the items
You may want to take a look at Splice

Related

AngularJS - Callback after ng-repeat update

I got some trouble understanding how I make a callback after I've updated an ng-repeat. I basically want to be able to make a callback function after my updates to my ng-repeat has been finished. Currently have this:
var app = angular.module('myApp', []);
app.directive('onLastRepeat', function() {
return function(scope, element, attrs) {
if (scope.$first)
console.log("ng-repeat starting - Index: " + scope.$index)
if (scope.$last) setTimeout(function(){
console.log("ng-rpeat finished - Index: " + scope.$index);
}, 1);
};
});
app.controller('MyController', function($scope) {
$scope.data = [1,2,3,4,5,6,7,8,9,10,12,12,13,14,15,16,17,18,19,20];
$scope.buttonClicked = function() {
console.log('Btn clicked');
$scope.randomItems = getRandomItems(this.data.length);
};
});
HTML
<div ng-app="myApp">
<div ng-controller="MyController">
<button ng-click="buttonClicked()">Highlight random</button>
<ul class="item" >
<li ng-repeat="item in data" ng-class="{highlight: randomItems.indexOf($index) > -1}" on-last-repeat>{{ item }} </li>
</ul>
</div>
</div>
Link to fiddle: https://jsfiddle.net/hbhodgm3/
So how the "app" works is that it lists the content of the data-array then when you click the "highlight"-button it randomly highlights 2 in the list. So my problem is that I want to have a callback function for when the highlighting/DOM-render is done. I found a way to do this for the initial ng-repeat with $scope.first and $scope.last to check when ng-repeat is done, but doesn't seem to work with the highlighting.
Hope I managed to explain the problem,
Thanks in advance.
See $q and Promises for a better understanding of how to work with the asynchronous nature of angular.
Presuming getRandomItems(this.data.length); is an API call that could take seconds to perform:
asyncItems(this.data.length).then(function(randoms){
$scope.randomItems = randoms;
//handle post rendering callback
});
function asyncItems(length) {
var deferred = $q.defer();
var items = getRandomItems(length);
if (items){
deferred.resolve(items);
}
else {
//no items :(
deferred.reject([]);
}
return deferred.promise;
}

Ng-repeat using $scope from a different state?

I have two views, the first one (Search) has a button which when clicked will add an item to $scope.results1 and then take the user to the other view (Results) where the ng-repeat is.
When I click the button and the results page comes up, only "1" is displayed. However, if I call the test function straight away in the controller, I get taken to the Results page and both "1" and "2" are displayed. In both cases, the console log shows that the array results1 contains 2 items.
From what I've read, the solution would be to implement either a factory or a service but I'm fairly new to Ionic/angular so not quite sure how to begin such an implementation, any pointers would be appreciated!
Button in Search view :
<button class="button-full" id="find" ng-click="test();">Find</button>
SearchController:
$scope.results1=[];
$scope.results1.push(1);
$scope.test = function(){
$scope.results1.push(2);
console.log("pushed 2");
console.log($scope.results1);
$state.go("tab.results");
};
Results view:
<ion-content ng-controller="SearchController">
<body>
<div id="results">
<div class="list" id="search-items">
<div ng-repeat="item in results1">
{{item}}
</div>
</div>
</div>
</body>
</ion-content>
You could implememnt a service for holding Results like this
var mainApplicationModule = angular.module("yourAppName");
mainApplicationModule.service('ResultService', function(){
var results = [];
this.add = function(data){ // to add data to results
results.push(data);
}
this.getResults = function(){ // to get all results
return(results);
}
})
Inject ResultService into your SearchController like this,
mainApplicationModule.controller('SearchController',['$scope','ResultService','$location', function($scope,ResultService,$location) {
ResultService.add(1) // Adds 1 to 'results' array in ResultService
$scope.test = function() {
ResultService.add(2); // Adds 2 to Results array in ResultService
$location.path("/results") // replace with path to your results view
}
$scope.results1 = ResultService.getResults(); // will have [1,2]
}
you can pass data while changing state,
config the state like this:
.state('tab.results', {
url: '/yoururl',
templateUrl: 'yourtemplate',
controller: 'yourcontroller',
params: {
"results": ""
}
});
then using :
$state.go("tab.results",{"results": $scope.results1});
in the second controller inject $stateParams and get value:
$scope.results = $stateParams.results;

Saving Updates to AngularFire $firebaseArray in a Service Called from a Directive

I am pulling my hair out trying to figure this out, I feel like I've tried everything to get this working.
I am creating essentially a todo app. I am using AngularFire in a service. There is an item directive that is repeating items that each have a checkbox. When a checkbox is checked/unchecked it should call a method on the DataService to update the item object {completed: false} to {completed: true} or vice versa each time it's checked/unchecked. ngChange passes in the individual object from an array of objects (objects are representing a unique item). Everything seems to work except being able to save the updated item after the checkbox is checked/unchecked. Everything I've tried has given for example, this error: "Invalid record; could determine key for somekey"
In the screenshot below, the console says it updates the object, but throws an error on trying to save the updated entry to firebase. Everything I've tried passing in has given the same error. What am I doing wrong here? Please take a look at the code below. I've spent a long time fiddling with this, so any help would be greatly appreciated. Please if anything doesn't make sense, ask me instead of downvoting, I'm really trying to explain this the best I can and really need help. Thank You!
Service Code:
var app = angular.module('dataService', ['firebase']);
app.factory('DataService', ["$firebaseArray",
function ($firebaseArray) {
//create, retrieve, update, destroy data from angularfire
var url = 'https://myFirebase.firebaseio.com/item';
var fireRef = new Firebase(url);
var factory = {};
factory.complete = function (data) {
var completeGoal = $firebaseArray(fireRef);
console.log("complete, fired in factory");
var itemID = data.$id;
console.debug("this item ID: " + itemID);
if(data.completed === true) {
console.log("this item data.completed: " + data.completed)
data.completed = false;
console.log("this item data.completed: is NOW " + data.completed)
}
else {
console.debug("this item data.completed: " + data.completed)
data.completed = true;
console.debug("this item data.completed: is NOW " + data.completed)
}
completeGoal.$save(itemID);
console.log(completeGoal.$save(itemID));
}
return factory;
}]);
Directive Code:
app.directive('item', function() {
return {
scope: {
item: '=set',
onClick: '&',
listType: '=',
complete: '='
},
controller: function() {},
controllerAs: 'ctrl',
bindToController: true,
transclude: true,
replace: true,
restrict: 'EA',
templateUrl: 'directives/items.html',
link: function(scope, elem, attrs) {
scope.$parent.onChange = function (data) {
scope.ctrl.complete(data);
}
//filter the items by type
var typeFilter = {type: scope.ctrl.listType}
scope.filterExpr = typeFilter;
}
}
});
Item Directive Template:
<div class="row goal-item" ng-repeat="item in ctrl.item | filter:filterExpr">
<div class="col-xs-12 col-sm-8 col-sm-offset-2">
<ul>
<li>
<label class="checkbox" ng-click="onChange(item)">
<input type="checkbox" class="goal-checkbox" ng-click="null" />
</label>
<span class="goal-title goal-completed">{{item.text}}</span>
</li>
</ul>
</div>
</div>
Controller:
app.controller('mainCtrl', ['$scope','DataService', function($scope, DataService) {
this.complete = DataService.complete;
}])
I think the problem here is that you're using the actual firebase key to try and save the data. According to the angularfire docs the $save method actually accepts 1 of 2 parameters:
$save(recordOrIndex)
So either the record - which would be the entire object or the index wich would be the indexed location inside of your local array [0,1,2,3,...]
I think your best bet would be to try and save the entire object opposed to using the unique key. So like this:
completeGoal.$save(data);
Hopefully it's that easy!
Here's the link to the angularfire api I'm referencing:
https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-firebasearray-saverecordorindex

AngularJS : How to add a new object into array while using factory and directives

Trying to simulate a hotel cart.
Newbie here
Questions
1. How to add an item to orders when clicked on corresponding Add button
2. Is it correct to use a factory for serving both menuitems for menu directive and orderItems for cart directive
3. On click of add button, where should the to be called add function be written, in the factory or in the directive's controller
4. Is there any way to better this code and its logic?
For those who wish to see the plunkr demo can view the same here
HTML snippet
<menu></menu>
JS snippet
angular.module('myApp',[])
.factory('menuItems',function(){
return {
list:function(){
var items = [{'name':'kabab'},
{'name':'chicken'},
{'name':'egg'},
{'name':'noodles'}];
return items
}
};
})
.factory('cartItems',function(){
var orders = [];
return orders;
})
.directive('menu',function(){
return {
restrict:'E',
template:"<ul><li ng-repeat='item in menuItems'>"+
'{{item.name}}' +
"</li></ul>",
scope:{},
controllerAs:'menuCtrl',
controller:function($scope, menuItems){
console.log("I am in menuDirective and outputting menuItems")
console.log(menuItems);
$scope.menuItems = menuItems.list();
},
link:function(){
}
}
})
.directive('cart',function(){
return{
restrict:'E',
template:"<ul><li ng-repeat='order in cartItems'>"+
'{{order.name}}' +
"</li></ul>",
scope:{},
controller:function($scope,cartItems){
$scope.cartItems = cartItems.list();
},
link:function(){
}}
})
Plunker Demo
I think this is a fine way to do it. Since you need to access the same dataset from multiple directives, it makes sense to me to put the methods and data into a factory.
HTML:
<body ng-controller="MainCtrl">
<menu></menu>
<br />Cart:<br />
<cart></cart>
</body>
JS:
angular.module('plunker',[])
.controller('MainCtrl', function() {
})
.factory('menuItems',function(){
return {
list:function(){
var items = [{'name':'kabab'},
{'name':'chicken'},
{'name':'egg'},
{'name':'noodles'}];
return items
}
};
})
.factory('cartItems',function(){
return {
orders: [],
add: function(item) {
this.orders.push(item)
}
}
//return this.orders;
})
.directive('menu',function(cartItems){
return {
restrict:'E',
template:"<ul><li ng-repeat='item in menuItems'>"+
'{{item.name}}' +
"<button ng-click='addItem(item.name)'>Add</button></li></ul>",
scope:{},
controllerAs:'menuCtrl',
controller:function($scope, menuItems){
console.log("I am in menuDirective and outputting menuItems")
console.log(menuItems);
$scope.menuItems = menuItems.list();
$scope.addItem = function(item) {
cartItems.add(item);
console.log(cartItems.orders)
}
},
link:function(){
}
}
})
.directive('cart',function(){
return{
restrict:'E',
template:"<ul><li ng-repeat='order in cartItems track by $index'>"+
'{{order}}' +
"</li></ul>",
scope:{},
controller:function($scope,cartItems){
$scope.cartItems = cartItems.orders;
},
link:function(){
}}
})
Any methods relating to the orders array should be put in the factory. Your factory should encapsulate all logic related to that particular 'thing'. Keeping the logic out of your directives gives you good abstraction, so if you need to change something, you know where all the logic is kept, rather than spread out in a bunch of directives. So if you want to add an 'empty cart' method, or a 'delete from cart' method, I would put those into the factory, operate directly on the orders array, and return the updated array to whatever is calling the methods.

angularjs: scope value doesn't get updated in view

there are buttons in detail.html file:
<div ng-controller="test.views.detail">
<div data-ng-repeat="item in details" scroll>
<button ng-click="showDetails(item)">The details</button>
in detail.js file
angular.module('test')
.controller('test.views.detail', function($scope) {
$scope.detailsClicked = false;
$scope.showDetails = function(item){
$scope.detailsClicked = true;
}....
in formDetail.html code:
<div ng-controller="test.views.detail">
{{detailsClicked}}
<div ng-if="detailsClicked">...
Initially it shows false for detailsClicked, when I click on button it goes to showDetails function but value of $scope.detailsClicked never get updated! It is straight forward not sure why it doesn't work:(
This is because you're using the same controller at two places and expecting the scope object to be the same which it is not. Everytime you call ng-controller in your markup a new scope object will be created. If you want them to be based off the same data then use a service.
Here is an example
app.controller('test.views.detail', function($scope, detailsClicked) {
$scope.detailsClicked = detailsClicked;
$scope.showDetails = function(item){
$scope.detailsClicked.isClicked = true;
}
});
Create a factory/service which will retain the data, make sure the data is a
app.factory('detailsClicked', function(){
var data = {
isClicked: false
}
return data;
});

Resources