Avoiding Dupes when pushing values from form to array in AngularJS - javascript

I'm having problems avoiding dupes with my code. Here is a simplified example. I know the problem is in the array object being a reference of the same scope variable, but what is the best way to avoid it?
<div ng-app="myApp">
<div ng-controller="myCtrl">
<input type="text" ng-model="item" />
<div ng-repeat="item in items">
{{ item }}
</div>
<button ng-click="save()">Save</button>
</div>
</div>
<script>
angular.module('myApp', []).
controller('myCtrl', function ($scope) {
$scope.items = [];
$scope.save = function() {
$scope.items.push($scope.item);
}
});
</script>
Here is a fiddle that demonstrates the problem:
http://jsfiddle.net/u8Fuk/8/

Use track by syntax to fix this problem.
<div ng-repeat="item in items track by $index">
{{ item }}
</div>
Here is a fiddle.

Depends on what your goal is.
If you want to allow for duplicate values you need to change the code a bit as each item in the ngRepeat has to have a unique id. See the track by section here.
That would work like this:
<div ng-app="myApp">
<div ng-controller="myCtrl">
<input type="text" ng-model="item" />
<div ng-repeat="item in items">
{{ item.value }}
</div>
<button ng-click="save()">Save</button>
</div>
</div>
<script>
angular.module('myApp', []).
controller('myCtrl', function ($scope) {
$scope.items = [];
$scope.save = function() {
$scope.items.push({value:$scope.item});
}
});
</script>
See the updated fiddle here.
If you don't want to allow for the same values you need to search for it.
<div ng-app="myApp">
<div ng-controller="myCtrl">
<input type="text" ng-model="item" />
<div ng-repeat="item in items">
{{ item }}
</div>
<button ng-click="save()">Save</button>
</div>
</div>
<script>
angular.module('myApp', []).
controller('myCtrl', function ($scope) {
$scope.items = [];
$scope.save = function() {
var found = $scope.items.reduce(function(previous, i){
if ($scope.item === i) return true;
return previous;
}, false);
if (found){
alert('duplicate value');
}
else{
$scope.items.push($scope.item);
}
}
});
</script>
See the updated fiddle here.

Related

Insert $index into name attribute in an ng-repeat angularJS

In order for ASP.NET MVC to correctly bind a list of items on a form post, the name attribute has be along the lines of
name='Show.Days[0].OpenHour'
name='Show.Days[1].OpenHour'
The user can enter the number of days the show will be into a form field, which then updates the model and the ng-repeat.
I'd like to be able to insert the appropriate index into the name field, something like
name='Show.Days[$index].OpenHour'
Is this possible with angular?
Use name="Show.Days[{{$index}}].OpenHour". With this, AngularJS evaluates $index and replaces it with the correct value.
It seems that you forgot to wrap the expression with {{ and }} in the view. Do you need something like this?
var app = angular.module('myApp', []);
app.controller("myCtrl", function ($scope) {
$scope.Show = {
Days: [
{OpenHour: '8am'},
{OpenHour: '10am'}
]
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<div ng-repeat="day in Show.Days">
<input type="text" ng-model="Show.Days[$index].OpenHour" name="{{Show.Days[$index].OpenHour}}">
</div>
</div>
</div>
or like this?
var app = angular.module('myApp', []);
app.controller("myCtrl", function ($scope) {
$scope.Show = {
Days: [
{OpenHour: '8am'},
{OpenHour: '10am'}
]
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myCtrl">
<div ng-repeat="day in Show.Days">
<input type="text" ng-model="Show.Days[$index].OpenHour" name="Show.Days[{{$index}}].OpenHour">
</div>
</div>
</div>

How to get parent's parent's parent's id in Angularjs

I have a problem. I need to know how to get grandparent's ID in AngularJS.
I need "{{parent}}" to become "grand-parent".
(it should be <div id="me-and-my-grand-parent">)
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
var pid = document.getElementsByClassName("i-am-a-child");
var pid = this.parentNode.id;
if (this.parentNode&&this.parentNode.id)
var pid=this.parentNode.id;
$scope.parent = var pid;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div id="grand-parent{{$index}}" ng-repeat="item in items">
<div>
<div>
<div id="me-and-my-{{parent}}" class="i-am-a-child">
</div>
</div>
</div>
</div>
</div>
My actual code
<li ng-repeat="project in projects" ng-class="{active: project.childToggle, '': !project.childToggle,hasChild: project.children.length > 0 }" ng-dblclick="childToggleCt(project)" id="project-{{$index}}">
<div class="project-overview">
<header class="clearfix flip-area">
<span ng-if="!project.inCart" class="status dropdown-button warning pull-left" id="id-{{ParentIdShow}}" data-intro="Status bar" data-position="right">Pending</span>
And for now JS was like tis :
$scope.ParentIdShow = function(obj)
{
alert(obj.target.parentNode.parentNode.parentNode.id);
}
The answer to these types of "parent of my parent of my parent of my..." is to use the controller as syntax. Read more about it here. In short, it lets you do stuff like
<div ng-controller="ctrl1 as first">
<div ng-controller="ctrl2 as second">
...
<div ng-controller="ctrlN as Nth">
<div ng-repeat="i in arr">
{{first.property}}
{{second.otherProperty}}
{{Nth.nProperty}}
Note how you dont need any parent calls.

How can I take a input value and put into a variable in angularJS?

How can I take a input value and put into a variable in angularJS?
this is the working testing version on my page but I did javascript and jquery together with angularJS to make it work but it is still giving me errors.
I know there must be a way to take the input value and put it inside of a var value through angularJS with out writting it manually how I'm doing it.
http://www.jaysg.com/poster
index.html
<div class="header">
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>Find That Poster</h1>
<div class="searchbox">
<button ng-click="start()" class="searchButton" id="btnSearch" value="Search">Search</button>
<p></p>
<input onkeydown="if (event.keyCode == 13) document.getElementById('btnSearch').click()">
</div>
</div>
</div>
</div>
</div>
<div class="content" ng-controller="StoreController as store">
<div class="container">
<div class="row">
<div >
<div class="col-md-3 posterImg" ng-repeat="product in store.products.results">
<span><img ng-src="http://image.tmdb.org/t/p/w300{{product.poster_path}}" class="img-responsive"></span>
<div >
<!-- <h2> {{product.original_title}}</h2>-->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
app.js // angular
(function() {
var app = angular.module('store', ['store-products']);//main app ng-app
app.controller('StoreController',[ '$http', function($http){
var store = this;
store.products = [ ];
$(document).ready(function() {
$( "input" ).keyup(function() {
var value = $( this ).val();
$('button').click(function() {
$http.get("http://api.themoviedb.org/3/search/movie?api_key=2f6ab7c6dc3db52d34703aae308640ef&query=" + value ).success(function(data){
store.products = data;
});
});
})
.keyup();
});
}]);
})();
You need the ng-model attribute.
Example:
<input onkeydown="if (event.keyCode == 13) document.getElementById('btnSearch').click()" ng-model="search">
Then you can access the variable using $scope.search.
As I can see you are currently not injecting the $scope.
This is how to do it:
app.controller('StoreController', ['$scope', '$http', function($scope, $http){
See more in the docs.
It would also be advisable to rewrite your jquery event handlers to angular event handlers.
So instead of $('button').click(function() {, create a $scope.start() function that is bound to the click event of the search button.
to get the value of the variable use ng-model and to listen for the key down use ng-keydown
<div class="searchbox">
<button ng-click="search()" class="searchButton" id="btnSearch" value="Search">Search</button>
<p>
</p>
<input ng-keydown="press($event)" ng-model="searchField" >
</div>
and in your controller you call the functions you just bound to your dom elements
app.controller('StoreController', [
'$scope',
'$http',
function ($scope, $http) {
$scope.press = function (e) {
console.log(e.keyCode);
if (e.keyCode == 13) {
$scope.search();
}
};
$scope.search = function (e) {
$http.get("http://api.themoviedb.org/3/search/movie?api_key=2f6ab7c6dc3db52d34703aae308640ef&query=" + $scope.searchField )
.success(function(data){
$scope.products = data;
});
};
}
]);

supplying AngularJS controller data

Assuming a controller is there to manipulate some data on the scope, what is the best practice to supply the controller with that data?
For example, if I have a list of items I might want a ListController to manipulate the list, and an ItemController to manipulate an individual item. But how do I give each ItemController an item to use?
In the case below, how would doSomethingToItem access the item. When nested in the ngRepeat the item is $scope.item, but in the selection view the item we want to manipulate is $scope.selection.
angular.module('MyApp', [])
.controller('ListController', function($scope, $http) {
$scope.list = $http.get(...);
$scope.selection = null;
})
.controller('ItemController', function($scope) {
$scope.doSomethingToItem = function() {
...
};
})
<div ng-controller="ListController">
<div ng-repeat="item in list" ng-click="selection = item">
<div ng-controller="ItemController">
<button ng-click="doSomethingToItem()">Do Something</button>
</div>
</div>
<div ng-show="selection"
<div ng-controller="ItemController">
<button ng-click="doSomethingToItem()">Do Something</button>
</div>
</div>
</div>
Isn't this a common structure, or is my thinking backwards?
You should understand that Angular would create n+1 ItemController in your case. N for items and 1 for the selection section.
To make passing the object that needs to be worked on easier you can change the method signature to
doSomethingToItem(item) and in html do
<button ng-click="doSomethingToItem(item)">Do Something</button> at both places.
Or else for the repeat case the item variable contains your object that you can access in ItemController
selection variable contains the reference to the select controller, which can be reference from the instance of the controller defined under selection section.
Update: The expression in ng-repeat and selection would differ
<button ng-click="doSomethingToItem(item)">Do Something</button>
and
<div ng-show="selection"
<div ng-controller="ItemController">
<button ng-click="doSomethingToItem(selection)">Do Something</button>
</div>
</div>
You could pass the item data model like this:
<div ng-init="instance = item" ng-controller="ItemController">
</div>
"instance" will be a reference to list array data model item in "ListController".
And you could access its property in your ItemController function closure:
.controller("ItemController", function($scope){
$scope.instance={};
$scope.doSomething = function(){
console.log($scope.instance.name);
}
$scope.$watch('instance',function(){
console.log("iitem changed");
},true);
});
I'm not quite sure what feature do you want to achieve in your "selection" implementation.
I think you want to implement a selected list and list item will be added to it when user clicked the list item. You could try to create a "selected list" model to control the selected list view if you want to add the selected item to a list.
ListController.js
.controller("ListController", function($scope){
$scope.selectedList = [];
$scope.addItem = function(item){
$scope.selectedList.push(item);
}
});
HTML
<div ng-repeat="selected in selectedList">
<div ng-init="instance = selected" ng-controller="ItemController">
<span>{{instance.name}}</span>
<button ng-click="doSomething()">selectedAction</button>
</div>
</div>
I wrote a simple multi-selected list example as following:
HTML
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="UTF-8">
<title>Nested controller</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script>
<script src="js/nestedController.js"></script>
</head>
<body>
<div ng-controller="parentCtrl">
<h2>List</h2>
<div ng-repeat="item in list">
<input type="checkbox" ng-model="item.selected" ng-checked="item.selected"/>
<div ng-init="instance = item" ng-controller="childCtrl">
<span>{{instance.name}}</span>
<button ng-click="doSomething()">doSomething</button>
</div>
</div>
<h2>Selected</h2>
<div ng-repeat="selected in selectedList">
<div ng-init="instance = selected" ng-controller="childCtrl">
<span>{{instance.name}}</span>
<button ng-click="selectedAction()">selectedAction</button>
</div>
</div>
</div>
JS
angular.module("myApp",[])
.controller("parentCtrl",function($scope){
//test data
$scope.list = [{name:'item1',age:'12',selected:false},{name:'item2',age:'18',selected:false}];
//use model to control selected list view
$scope.selectedList = [];
//refresh the selected list model when the list checked stauts has been updated
$scope.$watch('list',function(){
console.log("parent controller detected change");
$scope.selectedList = [];
$scope.list.forEach(function(elem,index,array){
if(elem.selected===true){
$scope.selectedList.push(elem);
}
});
},true);
})
.controller("childCtrl",function($scope){
$scope.instance={}
$scope.doSomething = function(){
alert("I'm the item: "+$scope.instance.name);
}
$scope.selectedAction = function(){
alert("I'm the selected item: "+$scope.instance.name);
}
//could register a watcher to monitor the model status
$scope.$watch('instance',function(){
console.log("child controller detected change");
},true);
});
Here is the jsFiddle demo
Hope this is helpful.

Why does my View not update in Angularjs on click, though I am able to get the content?

<div class="test" ng-controller="Ctrl">
<div ng-repeat="task in tasks">
<button ng-click="removeTask(task.id);">remove</button>
<div class="content">{{taskId}}</div>
</div>
<div>
var app = angular.module('app', []);
function Ctrl($scope) {
$scope.tasks = [{id:1,'name':'test1'}, {id:2,'name':'test2'}, {id:3,'name':'test3'}];
$scope.removeTask = function(taskId){
alert("Task Id is "+taskId);
};
}
The content I get in alert needs to be put in div, but the div won't get updated, what am I not doing correctly?
jsFiddle Demo
If you want id of task - task.id. taskId is just name for function parameter, it's undefined outside this function.
<div class="content">{{task.id}}</div>
But I suppose, best practice would be to pass whole object to click function:
$scope.removeTask = function(task){
alert("Task Id is " + task.id);
};
http://jsfiddle.net/PSz7t/3/
There different way's how you can achieve what are you looking for.
Here is mine. Since you need write an 'alert' for each removed item you need to save the status for each item so you can decide to show the alert or not
<div class="test" ng-controller="Ctrl">
<div ng-repeat="task in tasks">
<button ng-click="removeTask(task);">remove</button>
<div class="content"> <span ng-show="task.status=='deleted'">Task Id is {{task.id}} </span> </div>
</div>
<div>
http://jsfiddle.net/PSz7t/9/
var app = angular.module('app', []);
function Ctrl($scope) {
$scope.tasks = [{id:1,'name':'test1',status:'active'}, {id:2,'name':'test2',status:'active'}, {id:3,'name':'test3',status:'active'}];
$scope.removeTask = function(task){
task.status='deleted';
};
}

Categories

Resources