I have a PhoneGap + Onsen UI + AngularJS app in the works, where I have a list in the view, where the items will be fetched from the controllers variable.
I want to be able to remove items from this list, by clicking on them.
The list looks like this:
<ons-list>
<ons-list-item modifier="tappable" class="item" ng-repeat="citem in completeditems" ng-click="delete(citem)">
<ons-row>
<ons-col>
<div class="titlediv">
<header>
<span class="item-title">{{citem.name}}</span>
</header>
</div>
<div class="item-dates">
<span class="item-start">{{citem.start}}</span>
</div>
</ons-col>
</ons-row>
</ons-list-item>
</ons-list>
The completeditems object in the $scope looks like this:
var completeditemname = "item" + i;
$scope.completeditems[completeditemname] = {
id : "ID",
name : "Name for it",
start: "Start date"
}
Tried the following method, but it didn't work out:
$scope.delete = function(item) {
var index = $scope.completeditems.indexOf(item);
$scope.completeditems.splice(index,1);
//$scope.completeditems.remove(item); //tried this aswell
$scope.$apply() //i need this to update the view
}
You do not need the $scope.$apply() invocation. As you are making alterations to scope variables the digest cycle will be triggered anyhow and you will be encountering an error because of this I believe.
UPDATED:: You're working with an actual object by the looks of it so I've updated the code in the plunker to help you out. It means altering the ng-repeat to use both key and value.
Here is a simple plunkr showing a basic example of what you are trying to do with a one liner in the delete function http://plnkr.co/edit/NtQD....
<body ng-app="myApp">
<div ng-controller="myController as ctrl">
<ul ng-repeat="(key, value) in ctrl.items track by key">
<li ng-click="ctrl.delete(key)">{{value}}</li>
</ul>
</div>
</body>
var myApp = angular.module('myApp', [])
.controller('myController', [
'$scope',
function($scope) {
var self = this;
self.items = {
item1: {
id: 1,
name: 'a'
},
item2: {
id: 2,
name: 'b'
},
item3: {
id: 3,
name: 'c'
}
};
self.delete = function(key) {
delete self.items[key];
};
}
]);
Hope that helps you out!
$scope.$apply() should only be used when changes are coming in from outside the Angular framework. Since your delete() function is being called from an ng-click, it is already being managed by Angular and calling $apply() will raise a "$digest is already in progress" error (check your browser console). Removing that call will most likely get your code working.
Related
I have to call the ng-click function with the onClick but in my case ng-click function is not
//Controller function
$scope.editProductDetail = function(productObject) {
$scope.getProduct = productObject;
}
<a href="#"
onclick="document.getElementById('editProduct').style.display='block'" ng-click="editProductDetail(list)" target="_self">
</a>
call but model is open with onClick function?
It seems you're trying to set a class to the selected item from a product list, it seems you're confusing some AngularJS concepts.
If you're using AngularJS there's no need to use both onclick and ng-click.
If you want to show all products from your list you may want to use ng-repeat.
You need to initialize your Module for your AngularJS controller to load, and the controller must be within the module in the HTML code.
I've done an example bellow based on your code, it might help if you edit your answer and add your complete code.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.count = 0;
$scope.selectedIndex = 0;
$scope.editProductDetail = function (index) {
$scope.selectedIndex = index;
};
$scope.productList = [
{ name: 'Product 1', price: '1,00 U$' },
{ name: 'Product 2', price: '2,00 U$' },
{ name: 'Product 3', price: '3,00 U$' }
];
});
.selected-item {
display: block;
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl" >
<h2>Selected Item Number: {{ selectedIndex + 1}}</h2> <!-- I've added +1 since index starts at 0 -->
<div ng-repeat="item in productList">
{{ item.name}} {{item.price}}
</div>
</div>
As debabrata was saying you don't need to use both, just use something like this:
// Your controller
$scope.editProductDetail = function(productObject) {
$scope.setDisplayBlock = true;
$scope.getProduct = productObject;
}
// Your HTML
<span id="editProduct" ng-class="{'css-class-with-display-block': setDisplayBlock}">The element to change</span>
I've gone through what must be 20 similar questions asked on SO but have yet to find a solution for my situation, so I hope you guys can help me out.
The goal is to sort the list of names by the property that's provided in the "sort-type" attribute of the directive, but only for the list within each controller (not all lists at the same time).
HTML
<div ng-controller="TestController as testOne">
<b>By:</b> {{testOne.sortType}}<br>
<b>Reverse:</b> {{testOne.sortReverse}}<br>
<div ng-repeat="item in testOne.list">
<p table-sort sort-type="name" sort-reverse="false">Sort by name</p>
<ul>
<li ng-repeat="childItem in testOne.childList | orderBy:testOne.sortType">{{childItem.name}}</li>
</ul>
</div>
</div>
<br><br>
<div ng-controller="TestController as testTwo">
<b>By:</b> {{testTwo.sortType}}<br>
<b>Reverse:</b> {{testTwo.sortReverse}}<br>
<div ng-repeat="item in testTwo.list">
<p table-sort sort-type="name" sort-reverse="false">Sort by name</p>
<ul>
<li ng-repeat="childItem in testTwo.childList | orderBy:testTwo.sortType">{{childItem.name}}</li>
</ul>
</div>
</div>
Javascript (Angular)
var app = angular.module('demo', []);
app.controller('TestController', TestController);
function TestController() {
var vm = this;
vm.sortType = 'oldOrder';
vm.sortReverse = false;
vm.list = [1];
vm.childList = [{ name: 'Jimmy' },
{ name: 'Danny' },
{ name: 'Bobby' }];
}
/////////////////////////////////////
app.directive('tableSort', tableSort);
function tableSort() {
var directive = {
restrict: 'A',
link: linkFunc,
};
return directive;
function linkFunc(scope, element, attr) {
element.on('click', function() {
if(scope.sortType === attr.sortType) {
scope.sortReverse = !scope.sortReverse;
} else {
scope.sortType = attr.sortType;
}
});
}
}
JSFiddle here
My actual application is a bit more complex but I've tried to abstract it as much as possible.
Thanks for looking :)
Ok Several things going on here:
you are using the controllerAs syntax on your templates but
you are changing scope variables in your directive. hence your
controller variables are never changed.
your directive is inside of the ng-repeat which means that
you are actuating actually on a child scope so if you are setting
variables directive on the scope your ng-repeat won't be able to
reach them because they are being set after the child scope are
created.
you are using element.on which executes outside of angular
digest which means you would have to call scope.$apply to let
angular know that something happened.
Take a look at this
https://jsfiddle.net/rez8ey12/
i hope it helps
I'm having difficulties in solving this. What I'm trying to achieve is to update iterated objects which is passed in to a function in a different controller.
Here is my controllers -
angular.module('eatmapp.controllers', ['eatmapp.services'])
.controller('AppCtrl', function($scope) {
$scope.intoCart = function(item) {
if(item.type == 'variations'){
item = newItemObj;
}
}
})
.controller('BrowseCtrl', function($scope, dataService, $localstorage) {
dataService.getItems().then(function(returnData) {
$scope.items = returnData.products;
})
});
Here is my view -
<div ng-controller="BrowseCtrl">
<div class="list card product" ng-repeat="item in items" ng-click="intoCart(item)">
<div class="item item-text-wrap">
<span class="ifs-productcat" ng-repeat="category in item.categories">{{category}}<span ng-if="$index != item.categories.length - 1">,</span></span><br>
<h3>{{item.title}}</h3>
<h3>Rs.{{item.price}}</h3>
</div>
</div>
</div>
I need to update item object with newItemObject in iteration(ng-repeat) implemeted in template view after doing some condition check with method (intoCart) in another controller(AppCtrl). I'm fairly new to javascript programming and I'm looking for some help.
The problem I had was not able to get access to 'ng-repeat' child scope in controller.
I solved this using 'this.item' in controller rather than passing the whole object or index.
HTML -
<div class="list card product" ng-repeat="item in items" ng-click="intoCart()"></div>
Controller -
angular.module('eatmapp.controllers', ['eatmapp.services'])
.controller('AppCtrl', function($scope) {
$scope.intoCart = function() {
item = this.item; // current (ng-click) child scope of ng-repeat
if(item.type == 'variations'){
item = newItemObj;
}
}
})
Now, whenever I made changes to 'item' object, it automatically updates scope in view (ng-repeat).
Once way I like to handle this is by using services as setters and getters. The problem is you have to include the service with every controller that needs to access it, but if you don't have too many it's no big deal. So something like this:
.service('userFirstName', function() {
var userFirstNameProp;
return {
getter: function() {
return userFirstNameProp;
},
setter: function(value) {
userFirstNameProp = value;
}
};
})
Then you can call userFirstName.getter() or userFirstName.setter("John") as appropriate.
While working on the phone tutorial i wondered how to implement this use case
user clicks on add phone
the function addItem is fired and the phone.id is passed
the relevant phone is retrieved and the quantity increased by 1
the increased quantity should be displayed in the input
You can find my codepen demo here and this is the relevant code
<ul class="phones">
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp">
<b>{{phone.name}} </b>
<i ng-click="addItem(phone.id)"> add phone</i>
<input name='{{phone.id}}'
value='{{phone.qty}}'
ng-readonly='{{phone.orderReadonly}}' /><br />
<p>{{phone.snippet}} </p>
</li>
</ul>
and the javascript
var phonecatApp = angular.module('phonecatApp', []);
phonecatApp.controller('PhoneListCtrl', function($scope) {
$scope.phones = [
{'id': 1, 'name': 'Mui 1'
,'snippet': 'Our newcomer from asia.'
,'orderReadonly' : 'false', 'qty': 4}
....
,{'id': 4, 'name': 'Msft Lumiaâ„¢'
,'snippet': 'Who knows what windows 10 will bring'
,'orderReadonly' : 'true','qty': 2}
];
$scope.orderProp = 'id';
$scope.addItem = function(phone_id) {
// from http://stackoverflow.com/questions/15610501/
var found = $filter('filter')($scope.phones, {id: phone_id}, true);
if (found.length) {
found[0].qty = found[0].qty + 1;
} else {
$scope.selected = 'Not found';
}
}
});
Current status
passing the id works
finding the phone does not work: var found = $filter('filter')($scope.phones, {id: phone_id}, true); // found in http://stackoverflow.com/questions/15610501/
increasing quantity does not work
My questions are
if and how onclick / ng-click should be used in the angular way
how to solve my requirement - increase phone quantity onclick on <i>add phone</i>
I don't know why qty doesn't work - it should, unless your filter doesn't find a match.
But you shouldn't even be doing this. Instead of passing the id of the object and then locating the object to change its property, just pass the object phone itself:
<i ng-click="addItem(phone)"> add phone</i>
Then, in the controller, simply do this:
$scope.addItem = function(phone) {
phone.qty = phone.qty + 1;
}
Summarising the two above answers. The "++" increment will not work on the variable or object property from the "ng-click" directive, so instead you should use:
variable = variable + 1
And in connection to the original question the
<i ng-click="phone.qty = phone.qty + 1"> add phone</i>
will do the trick.
To answer your first question:
Using ng-click runs an angular expression inside Angular's scope. If you use the onclick simply runs javascript code.
So if you have some variable 'numPhones' initialized inside your controller, then you can have:
ng-click="numPhones = numPhones + 1"
and the numPhones variable will be incremented.
On the other hand:
onclick="numPhones = numPhones + 1"
doesn't reference the surrounding angular scope.
So if you're using Angular, you probably wouldn't want onclick at all.
Here's an example:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script>
angular.module('phones', [])
</script>
</head>
<body ng-app='phones'>
<div>
{{ numPhones }}
<button ng-click="numPhones = numPhones + 1"> add 1</button>
</div>
</body>
</html>
I have this code in my application:
$scope.appendBets = function()
{
$.each($scope.phaseBets, function(i, bet)
{
var betElement = angular.element('<div ng-model="phaseBets[i]">Bet id: {{phaseBets[i].id}}</div>');
angular.element(document.querySelector('#betsHolder')).append(betElement);
$compile(betElement)($scope);
});
}
the $scope.phaseBets is loaded from $http.get.
Now the problem is that the {{phaseBets[i].id}} content not seen on the html page, I am getting this Bet id:.
I have seen this OS but it's not working for me, maybe because of the array.
Is there anything wrong with my code?
Note
The thing is that by the I'd I will create an element (different for each id) so ng-repeat not helping that mutch.
Here's how I'd do it using ng-repeat and ng-include:
$scope.items = [
{id: 1, title: 'foo', data: {body: 'baz1'}},
{id: 2, title: 'bar', data: {body: 'baz2'}}
];
<div ng-repeat="item in items">
<h2>{{item.title}}</h2>
<div ng-include src="getTemplateById(item.id)"></div>
</div>
Where the templates are defined inline like this:
<script type="text/ng-template" id="template-1.html">
Content of template-1.html
<div>{{item.data.body}}</div>
</script>
<script type="text/ng-template" id="template-2.html">
<p>Content of template-2.html</p>
</script>
and getTemplateById is:
$scope.getTemplateById = function(id) {
return 'template-' + id + '.html';
};
You can see it in action here.
I think you got it from wrong side, in angularjs controllers/data drives the view, here you are creating elements (and even worse adding them to page) in loop (expensive DOM operations)
I'd replace your code with following
<div id="betsHolder">
<div ng-repeat="bet in phaseBets track by bet.id">Bet id: {{bet.id}}</div>
</div>
as soon as you assign your array/object to $scope.phaseBets the DOM will be created
but using ng-repeat is better option,
angular.forEach($scope.phaseBets, function(bet, i)
{
var betElement = angular.element('<div ng-model="bet">Bet id: {{bet.id}}</div>');
angular.element(document.querySelector('#betsHolder')).append(betElement);
$compile(betElement)($scope);
});