AngularJS: Hitting a strange issue with $scope variables - javascript

In the following code, whenever you delete an item from the delete link in the list, it will only delete the item from the list, but it will not delete the currently selected item. (The item displaying once you click on it). However, if you click on the delete link next to the currently selected item, it will delete from both places.
To replicate what I'm seeing:
Add a bunch of items by typing in the text box and hitting enter a few times.
Select one of the items from the list.
Click delete next to the item when it displays below.
This is the correct behavior.
Select another item you created earlier.
Now click the delete link next to the item in the list.
The item is removed from the list, but not the currently displayed item.
When I step into the code $scope.currentUser is undefined when I click on the delete link in the list.
Why is this happening?
<html ng-app="myApp">
<head>
<script src="http://code.angularjs.org/1.0.1/angular-1.0.1.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.config(function($routeProvider) {
$routeProvider.when('/User/:id', {
controller: UserCtrl,
template: '<h1>{{currentUser.name}}</h1> <a ng-click="deleteUser(currentUser.id)">delete me</a>'
});
});
app.factory('userSvc', function(){
return new UserService();
});
function UserCtrl($scope, $routeParams, $location, userSvc) {
var currUser = userSvc.getUser($routeParams.id);
$scope.currentUser = currUser;
$scope.users = userSvc.getAllUsers();
$scope.addUser = function () {
var user = {
id: userSvc.nextId(),
name: $scope.addUserName
};
userSvc.addUser(user);
$scope.addUserName = '';
$location.url('/User/' + user.id);
};
$scope.deleteUser = function(id) {
if($scope.currentUser != null && $scope.currentUser.id == id) {
$scope.currentUser = null;
}
userSvc.delete(id);
};
};
function UserService() {
var users = [{id: 1, name: 'Ben' }];
this.delete = function(id) {
for(var i = 0; i < users.length; i++) {
var user = users[i];
if(user.id == id) {
users.splice(i,1);
}
}
};
this.addUser = function(user) {
users.push(user);
};
this.getAllUsers = function() {
return users;
};
this.getUser = function(id) {
for(var i = 0; i < users.length; i++) {
var user = users[i];
if(user.id == id) {
return user;
}
}
};
this.nextId = function() {
var maxId = 0;
for(var i = 0; i < users.length; i++) {
var user = users[i];
maxId = Math.max(maxId, user.id);
};
return maxId + 1;
};
}
</script>
</head>
<body>
<div ng-controller="UserCtrl">
<form ng-submit="addUser()">
<input ng-model="addUserName" type="text"/>
<input type="submit" value="Add"/>
</form>
<ul>
<li ng-repeat="user in users">{{user.name}} <a ng-click="deleteUser(user.id)">delete</a></li>
</ul>
</div>
<div ng-view></div>
</body>
</html>

It turns out that selecting a user from the list actually also created a brand new scope that was seperate from the one used to bind the list.
Thanks to Gloopy's comment above to check out Batarang, I was able to see this happen. If this happens to help you, please +1 his comment.
According to the documentation on Scopes some directives will actually cause a new scope to be created. I'm assuming that clicking a link that is being handled by the $routeProvider also results in the creation of a whole new scope tree, likely because it's creating another instance of that controllor.

Related

$localStorage array select with ng-options not working properly?

This is my code. i am using $localStorage for pushing an object into array. when i clicking the button the object is pushed properly and splicing the same same object again click on the same button. $localStorage.tableArray assign to the $scope.Storage for dropdown list. Drop down list coming good when the button action done.
My problem is the $scope.$storage having two items. if i refresh the page dropdown list not came.
if i pushing or splicing action performed on the buttons drop down list coming good.
please help how to get $scope.$storage items into the dropdown list when refreshing the page.
I Create a plunker regarding this. check once
HTML:
<body ng-controller="MainCtrl">
<a class="btn {{table.btnClass}} btn-success" ng-repeat="table in tablelist" ng-click="getTable(table)" style="padding-left:1px">{{table.tablename}}</a>
<select ng-options="table.tablename as table.tablename for table in $storage" ng-model="table.tablename"><option value="">---select table---</option></select>
JS:
var app = angular.module('plunker', ["ngStorage"]);
app.controller('MainCtrl', function ($scope,$localStorage,$filter) {
$scope.tablelist = [{ "tablename": "t1" }, { "tablename": "t2" },{ "tablename": "t3" },{ "tablename": "t4" }]
if ($localStorage.tableArray === undefined) {
$localStorage.tableArray = []
}
if ($localStorage.tableslist === undefined) {
$localStorage.tableslist = []
}
angular.forEach($scope.tablelist, function (list, $index) {
var found = $filter('filter')($localStorage.tableArray, { tablename: list.tablename }, true);
if (found.length) {
$scope.tablelist[$index].btnClass = found[0].btnClass;
}
});
$scope.getTable = function (table) {
table.btnClass = table.btnClass == "btn-danger" ? "btn-success" : "btn-danger"
var exists = false;
angular.forEach($localStorage.tableArray, function (list, $index) {
if ((list.tablename == table.tablename)) {
console.log(list.tablename)
console.log(table.tablename)
exists = true;
$localStorage.tableArray.splice($index, 1)
$localStorage.tableslist.splice($index, 1)
$scope.$storage= $localStorage.tableArray;
console.log( $scope.$storage)
return false
}
});
if (!exists) {
$localStorage.tableslist.push(table)
$localStorage.tableArray = $localStorage.tableslist;
$scope.$storage = $localStorage.tableArray
console.log($localStorage.tableArray)
table.color = "red"
}
}
});
https://plnkr.co/edit/0RpAGVR5ZpVFMvmmxipu?p=preview
As per my understanding you want your dropdown list to be initialized on refresh with the stored value from your localstorage.
Adding below line in controller works for me:
$scope.$storage = $localStorage.tableArray
Check plnkr

Adding and removing items from arrays, based on a condition in AngularJS

I'm trying to make a dynamic "favorites" toggle-button.
When clicking the button, it should add the selected player to the users favorite-list. If the player is already favorited, it should remove it.
I've also tried to iterate through the favorites, to check if a player is already favorited. If true, it colors the favorite star gold.
A few problems here. My for loop for checking seems to be working properly as long as the array only contains one item. But as soon as I try adding more, the gold icon is only gold colored on the last player added to favorites. So the check only finds one favorite at a time, and I can add a player to favorites many times, as long as I vary the players I add.
If someone could point me in the right direction and help me understand why my loop isn't working correctly, that would be awesome!
http://codepen.io/utrolig/pen/LNgRwv
Javascript
angular.module('test', [])
.controller('TestController', function($scope){
$scope.players = [
{
uniqueid: "gem",
name: "Ole Christian",
cake: false,
},{
uniqueid: "utrolig",
name: "Stian",
cake: false,
},{
uniqueid: "drozo",
name: "Adrian",
cake: false,
}
];
$scope.user = {
name: "Stian",
username: "stiba",
favorites: [{uniqueid: "drozo"}],
}
$scope.checkFavorite = function(id){
fav = $scope.user.favorites;
var exists;
for (var i=0; i < fav.length; i++){
if(fav[i].uniqueid == id){
exists = true;
} else {
exists = false;
}
}
return exists;
}
$scope.toggleFavorite = function(id){
fav = $scope.user.favorites;
if(fav.length === 0){
var newfav = {uniqueid: id};
fav.push(newfav);
} else {
if($scope.checkFavorite(id) === true){
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) fav.splice(i, 1);
}
} else if ($scope.checkFavorite(id) === false) {
var newfav = {uniqueid: id};
fav.push(newfav)
} else {
console.log('Error!');
}
}
}
$scope.isFavorited = function(){
return true;
};
})
HTML
<body ng-app="test">
<div class="container" ng-controller="TestController">
<h3>Players</h3>
<div ng-repeat="player in players" class="player-cont">
<div class="player">
<div class="favorite" ng-click="toggleFavorite(player.uniqueid)" ng-class="{'active': checkFavorite(player.uniqueid)}">
<i class="material-icons">star</i>
</div>
<i class="material-icons player-icon">person</i>
</div>
<div>
<p ng-bind="player.uniqueid"></p>
<p ng-bind="player.name"></p>
</div>
</div>
<h3>Favorites</h3>
<div ng-repeat="favorite in user.favorites track by $index">
<h5>{{favorite.uniqueid}}</h5>
</div>
<p class="user">
{{user.favorites}}
</p>
</div>
</body>
There's a couple of errors in your code.
The first is checkFavorite, if you examine the code you'll see that only the last item is actually compared to id, since the exists flag is updated for each item. You need to "short circuit" the loop and return true as soon as you find a value.
btw, is* is a common name convention for checking boolean values.
$scope.isFavorite = function(id){
var fav = $scope.user.favorites;
for (var i=0; i < fav.length; i++){
if(fav[i].uniqueid == id){
return true;
}
}
return false;
}
Your toggle is also very verbose, if you "reduce" the code you end up with something like this
$scope.toggleFavorite = function(id){
var fav = $scope.user.favorites;
// no previous items, remove, OK
if(fav.length === 0) {
var newfav = {uniqueid: id};
fav.push(newfav);
return;
}
// if already a favorite, uncheck/remove
if($scope.isFavorite(id)) {
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) fav.splice(i, 1);
}
}
// otherwise add the item
// remember, isFavorite returns true of false, there is no third state
else { // if ($scope.isFavorite(id) === false) {
var newfav = {uniqueid: id};
fav.push(newfav)
}
}
This can be edited further, since the isFavorite function will return false if the list is empty, i.e. no need for the first if
$scope.toggleFavorite = function(id){
var fav = $scope.user.favorites;
// if already a favorite, uncheck/remove
if($scope.isFavorite(id)) {
for(var i = 0; i < fav.length; i++){
if (fav[i].uniqueid === id) {
fav.splice(i, 1);
// unless the item exists more than once, break the loop
break;
}
}
}
// otherwise add the item
else {
var newfav = {uniqueid: id};
fav.push(newfav)
}
}

AngularJs ng-repeat didn't updated after changing $scope variable

I have table with item list and modal window where i can change drug properties. When properties changed that drug have to remove from that list. But it's didn't remove.
Modal window:
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: $scope
}).result.then(function(data){
var index = _.findIndex($scope.drugs, {_id: data._id});
$scope.drugs.splice(index,1);
}
i think element didn't remove 'cause when i open modal window i create copy of my scope and then work with copy..
On that form i have refresh button that refresh all list.
$scope.refresh= function() {
$scope.drugs = UnresolvedDrugsService.query();
};
and it's didn't work too. I think it happens because i work with copy too.
Okey, i try to emit some event
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: $scope
}).result.then(function(data){
var index = _.findIndex($scope.drugs, {_id: data.data._id});
$rootScope.$emit('refreshDrug', index);
}
$rootScope.$on('refreshDrug', function(index){
$scope.drugs = [];
$scope.drugs.splice(index,1);
// $scope.drugs= UnresolvedDrugsService.query();
});
And it's not working.
Can you help me and describe what i doing wrong, thx!
UPD
modal window html
<form role="form" name="resolveDrugForm" ng-submit="saveResolvedDrug(drug) && $close(drug)">
........{some code, input's and label}......
<input type="submit" class="btn btn-primary" value="{{'COMMON.SAVE' | translate}}"/>
<button type="button" class="btn btn-default" ng-click="$dismiss()" >{{'COMMON.CANCEL' | translate}}</button>
code of ng-repeat
<tr ng-repeat="drug in drugs" ng-click="resolveDrug($index)">
<td>{{drug.productName || drug.description }}</td>
<td>{{drug.aiccode }}</td>
</tr>
and all method of controller:
$rootScope.$on('refreshDrug', function(index){
// $scope.drugs = [];
$scope.drugs.splice(index,1);
// $scope.drugs= UnresolvedDrugsService.query();
});
$scope.drugs= UnresolvedDrugsService.query();
$scope.refresh= function() {
$scope.drugs= UnresolvedDrugsService.query();
};
$scope.spliceEl = function(data){
var index = _.findIndex($scope.drugs, {_id: data._id});
$scope.drugs.splice(index,1);
return true;
};
$scope.saveResolvedDrug = function(drug){
DrugsService.addResolvedDrug(drug).then(function(data){
var index = _.findIndex($scope.drugs, {_id: data.data._id});
if(data.data.ingredients && data.data.ingredients.length > 0){
data.data.ingredients = JSON.parse(data.data.ingredients);
}
$scope.drugs.splice(index,1);
return true;
});
return true;
};
$scope.resolveDrug=function(index){
$scope.drug={};
var drugs = $scope.drugs;
$scope.drug=angular.copy($scope.drugs[index]);
var scope=$scope;
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: $scope
}).result.then(function(data){
console.log($scope.drugs);
var index = _.findIndex($scope.drugs, {_id: data._id});
//$scope.drugs.splice(index,1);
console.log($scope.drugs);
$rootScope.$emit('refreshDrug', index);
}, function(data){
}).finally(function(data){
$scope.refresh();
});
}
I didn't know why it didn't works with events. But if saveDrug in modal result but sumbit form - work fine.
Now code looks like
$scope.resolveDrug=function(index){
$scope.drug={};
var drugs = $scope.drugs;
$scope.drug=angular.copy($scope.drugs[index]);
var scope=$scope;
$modal.open({
templateUrl:'app/interactions/partials/modals/resolveDrug.html',
controller: 'DrugsListController',
scope: scope,
resolve: {
drug: function () {
return $scope.drug;
}
}
}).result.then(function(data){
}, function(data){
}).finally(function(data){
});
}
$scope.saveResolvedDrug = function(drug){
DrugsService.addResolvedDrug(drug).then(function(data){
var index = _.findIndex($scope.drugs, {_id: data.data._id});
if(data.data.ingredients && data.data.ingredients.length > 0){
data.data.ingredients = JSON.parse(data.data.ingredients);
}
$scope.drugs.splice(index,1);
return true;
});
return true;
};

Angularjs typeahead-input-formatter doesn't work when model value being set on page load

I have a textbox that has a typeahead and typeahead-input-formatter attributes, to offer user sugestion list when they start typing and to store selected value in angularjs model but display text for the selected value in the textbox, for axample if model value to be set "jsmith" the textbox would display "John Smith" and it works fine when user selects suggested value from suggestion list, however when I retrieve existing record and trigger typeahead-input-formatter with 'ng-init' it does go through the code, but doesn't set display value of the textbox:
HTML
<input ng-controller="TypeaheadCtrl" ng-init="travel.managerId"
ng-model="travel.managerId" type="text"
typeahead="suggestionEntry.userId as suggestionEntry.fullName for suggestionEntry in
activedirectory($viewValue)"
typeahead-input-formatter="formatLabel($model)">
app.js
travelApp.controller('TypeaheadCtrl', function ($scope, $http, limitToFilter) {
$scope.entries = [];
$scope.activedirectory = function (empName) {
return $http.get("api/activedirectory/GetActiveDirectoryEntries/" + empName).then(function (response) {
$scope.entries = response.data;
return limitToFilter(response.data, 20);
});
};
$scope.formatLabel = function (model) {
if ($scope.entries.length < 1 && model != undefined) {
$http.get("api/activedirectory/GetActiveDirectoryEntries/" + model).success(function (data) {
$scope.entries = data;
for (var i = 0; i < $scope.entries.length; i++) {
if (model == $scope.entries[i].userId) {
return $scope.entries[i].fullName;
}
}
}).error(function () {
alert('Error reading JSON file.');
});
}
for (var i = 0; i < $scope.entries.length; i++) {
if (model == $scope.entries[i].userId) {
return $scope.entries[i].fullName;
}
}
}
});
Please advise.

Resetting an input to initial value when a search result is clicked with AngularJS

I've a list on ng-repeat that displays a list of results from a $http query (bind to an input). I'd like both for the list to disappear when the user clicks on one of the results and for the initial empty value of the model to be restored.
Basically, the functionality is as follows:
User searches term, list displays results, user clicks on result, list disappears, user clicks on input again to make another search, list with new results appear.
So far I've managed to make the list disappear, but not to make it appear again when the user makes another search.
Here's the relevant code:
<input type="text" ng-model="name" ng-click="Research()"/>
<ul ng-hide="clicked" ng-show="retype">
<li ng-repeat="result in results" ng-click="getDetails(result.id)">{{result.title}}</li>
</ul>
And the JS:
function Ctrl($scope, $http) {
var get_results = function(name) {
if (name) {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=8').
success(function(data3) {
$scope.results = data3.results;
});
}
}
$scope.name = '';
$scope.$watch('name', get_results, true);
$scope.getDetails = function (id) {
$http.get('http://api.discogs.com/artists/' + id).
success(function(data) {
$scope.artist = data;
});
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=500').
success(function(data2) {
$scope.releases = data2.releases;
});
$scope.clicked = true;
}
function Research(){
$scope.retype = true,
$scope.name = '';
}
Plunkr is down, I'll make one as soon as possible. Any idea about what am I missing?
I tidied up your code a little bit. Please note that the div is shown only when artist is defined. So when it is set to undefined by the $scope.clear() method, the mentioned div is hidden.
Html part:
<div ng-controller="Ctrl">
<input type="text" ng-model="name" ng-focus="clear()"/>
<ul>
<li ng-repeat="result in results" ng-click="getDetails(result.id)">{{result.title}}</li>
</ul>
<div ng-show="artist">
<h1>Artist</h1>
<ul>
<li>{{artist.name}}</li>
<li>{{artist.release_url}}</li>
<li>{{artist.uri}}</li>
<li>{{artist.resource_url}}</li>
</ul>
</div>
</div>
JavaScript part:
var myApp = angular.module('myApp',[]);
function Ctrl($scope, $http) {
$scope.name = undefined;
$scope.artist = undefined;
$scope.results = undefined;
var search = function (name) {
if (name) {
$http.get('http://api.discogs.com/database/search?type=artist&q='+ name +'&page=1&per_page=8').
success(function(data3) {
$scope.results = data3.results;
});
}
}
$scope.$watch('name', search, true);
$scope.getDetails = function (id) {
$http.get('http://api.discogs.com/artists/' + id).
success(function(data) {
$scope.artist = data;
});
$http.get('http://api.discogs.com/artists/' + id + '/releases?page=1&per_page=500').
success(function(data2) {
$scope.releases = data2.releases;
});
}
$scope.clear = function () {
$scope.name = undefined;
$scope.artist = undefined;
$scope.results = undefined;
}
}
There is working JSFiddle.
Your Research function is unnecessary because you don't need ng-show and ng-hide same time...
secondly you set clicked to ok but never set it false again after your research done...
here is working PLUNKER
Try using just one ng-hide or ng-show, instead of both. Since you never set clicked back to false, it is probably overriding the retype.
Both functions are two-way, so you can just use ng-hide="clicked", and inside function Research, set $scope.clicked to false.

Categories

Resources