How can I access to the ng-repeat scope? - javascript

My ng-repeat creates different elements and I need to do some operations, like creating variables, only on the ng-repeat scope.
How can I retrieve the ng-repeat scope?
How can I do something like this?
<div ng-repeat="item in items">
<button onclick="load(item, $scope)"></button>
</div>
$scope has to be the ng-repeat scope only. Is this possible?
EDIT
that's the issue:
The code:
HTML
<div ng-repeat="item in items" class="container">
<div class="row">
Name: {{item.name}}
<select ng-model="item['id']"
ng-options="opt for opt in ids"
ng-change="loadTypes(item)"
>
<option value="">Default</option>
</select>
<select ng-model="item['type']"
ng-options="opt for opt in types"
>
<option value="">Default</option>
</select>
</div>
</div>
Controller
//Executed one time when module is loaded
$scope.init = function(){
//All ids on the DB
$scope.ids = getIdsFromServer();
}
//Executed when an Ids select changes
$scope.loadTypes = function(item){
//Possible types for an Id
types = getTypesFromServer(item.id);
/*
thisItemNgRepeatScope.types = getTypesFromServer(item.id) ?!??!! Something like this?
*/
}
The problem:
Ids it's the same in all the whole document and that's ok, it's works properly.
But I want to change the model for the second select (types) only on the ng-repeat scope. So when I change Id, I get all possible types for this Id, but only in the row where I am, where the select has been changed.
How can I do this?

the mighty 'this' in your load(item) function is the scope.

I assume that with "ng-repeat scope" are are refering to the parent scope of each individual ng-repeat loop. This can be done easily be referencing the parent scope of the current scope like this:
$scope.$parent

Related

Angular Drop-down inside of ng-repeat with additional logic needed for selected option

This is basically a 2 part question which is why some of the other answers I've found on StackOverflow don't sufficiently cover what I'm asking.
Also need to note: using Bootstrap.
My simplified html can be explained like this:
<div class="container-fluid">
<div class="row"> Headers </div>
<div class="ParentObjectDiv" ng-repeat="pobj in pobjlist">//pobj list is returned from call in controller
<div class="row"> ParentObject values </row>
<div class="ChildObjectContainer">
<div class="row"> Headers </div>
<div class="ChildObjectDiv" ng-repeat="cobj in pobj.cobjs">
<div class="row">
<div class="col-md-1">
<select ng-model="cobj.someproperty"
ng-options="option for option in optionlist">
//Here is the issue : the optionlist is returned from a separate call in the constructor
//The cobj.someproperty can be null, and when that's the case, I want a custom selection
//that says please select a property
</select>
</div>
</div>
</div>
</div>
</div>
</div>
Ok, so on to what I have tried.
Can't do a selectedOption = "whatever" in the controller for obvious reasons.
I have tried adding an angular expression that sets the property of cobj.someproperty to a value like "Value not know" if it detects that the property is empty/null.
This is probably coming down to a fundamental misunderstanding of angular on my part. I have tried putting the expression that assigns this "not known" value to the property in various expression directives and just in the html itself. It is not an option to have this "not known" value on the actual object in the database and the list of Pobj's and their Cobj's is very large so I don't think I can spare iterating the entire list structure to set it before rendering and then iterating it again in the ng-repeats. I'm hoping that I can have a quick expression that evaluates null and sets it. Something like:
{{cobj.someproperty = cobj.someproperty ? cobj.someproperty : "value not known"}}
But not sure if that's a valid expression and if it is where I would put it because so far I've tried putting it in the ng-model itself, an ng-if, and just a blank line of html all within the scope of the cobj in the ng-repeat and none are working.
I haven't "tried" this, but my preferred solution and the one I've been researching is to see if there is a way to instead of evaluating and changing the cobj.someproperty, to change the ng-model of the select to point at a dummy property and at the same time set the cobj.dummyproperty to cobj.someproperty if some property is set, otherwise to set it to "value not known".
Additional considerations :
The parentobject list can be very large (>1k) and has on average 3 childobjects (but can have anywhere from 1 to 20) so performance is an issue.
There will be many cases where the user will not want to set this property so "value not known" when null will not be a temporary thing.
A user also needs to be able to select "value not known" if they have later realize they were mistake about the cobj.someproperty.
I am going to work on the logic to push the changes back to the database later so I am not worried about how the value is stored in the model in angular because I will most likely not use the ng-model to update the field. Right now I'm just worried with the selected option of the drop down.
Here is the answer with simplified html.
JavaScript code
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.items = [{
"someProperty": "abc"
}, {
"someProperty": "xyz"
}, {
"someProperty": null
}]
$scope.optionlist = ["abc","xyz","pqr"];
});
HTML code:
<table ng-controller="MainCtrl" >
<tbody>
<tr ng-repeat="item in items track by $index">
<td width="75%">
{{ item.someProperty }}
</td>
<td width="25%">
<select ng-model="item.someProperty" >
<option value="">Value not know</option>
<option value="{{option}}" ng-repeat="option in optionlist">{{option}}</option>
</select>
</td>
</tr>
</tbody>
So whenever angular finds a null value for someProperty it binds the value to "Value Not Know". I am using ngRepeat instead of ngOption. Let me know if this is what you are looking for.
here is my plunk

Angular ng-repeat multiple row table with select

So I have this table with select boxes on each row. The problem is that when I make a change in one select box, all of them get changed showing the same value. What I need is to unbind all select boxes to be able to have different selections on each one of them.
Code goes like:
<tr ng-repeat="item in userMappings.items" ng-model="item.mapped">
<td class="col-md-3" ng-model="mappingType.name">
{{item.idsport}}, Game Type {{item.idgametype}}, {{item.name}} (Current limit {{item.value}})
</td>
<td class="col-md-3 mapType" style="padding-bottom: 25px; padding-left: 15px" >
<h4>Mapping Type: <span style="font-size:0.8em">{{selectedType}}</span> </h4> <br />
<select ng-model="mappingType.name" ng-change="which($index); selectedRow()" ng-options="item.name for item in mappingType"></select>
</td>
</tr>
When you use ng-model to bind to mappingTypes.name it will modify the property on the controller scope because that's where it's defined. mappingTypes isn't part of ng-repeat's scope so it goes through the scope inheritance chain and updates it there. So when you select an item, mappingTypes is now an array with a name property. All of your items bind to that same array, so they all have the same value.
If you want to only change a specific item in ng-repeat, you should use something like item.mapping. if you modify anything other than item it's modifying the entire controller's scope.

AngularJS: ng-if outside ng-repeat breaks ng-repeat

AngularJS Verion: 1.3.8
JSFiddle: http://jsfiddle.net/uYFE9/4/
I've been working on a small AngularJS application, and ran into a bit of a problem. I have an ng-repeat on a page, which fills in the contents of a form. The amount of items in the form is defined by a dropdown bound to a model, and populated using ng-options. Something like:
<select id="testAmount" ng-model="selectedItem" ng-options="item.name for item in items"></select>
<form role="form" name="testForm" ng-if="!complete">
<div ng-repeat="i in getNumber(selectedItem.number) track by $index">
{{$index}}
</div>
</form>
Complete is set to false in the beginning, and hitting a Next button will toggle complete and hide the form and dropdown. A Back button will then toggle complete back, and show the form again.
The problem I'm having is with the ng-if on the select (and previously, I had the form wrapped in a div with the same ng-if - same problem). The ng-repeat no longer updates when the select dropdown is changed. Removing the ng-if on the select restores the ng-repeat to working order.
I'm wondering if there's something strange I'm doing with the nesting here, or if it's actually a bug? You can test it out on the JSFiddle linked above. The $index should be printed the number of times on the dropdown, but isn't.
Interestingly enough - when debugging the problem on my local machine, having FireBug open fixed the issue.
This is because of ng-if creating a child scope and how prototypical inheritance works with primitives. In this case, the primitive is selectedItem that you are setting by the <select>, but is actually being set on the child scope and shadows/hides the parent scope property.
In general you should always use a dot (.) with ng-models:
$scope.selection = {selectedItem: undefined};
And in the View:
<div ng-if="!complete">
<select ng-model="selection.selectedItem"
ng-options="item.name for item in items"></select>
</div>
ng-if is causing you some scoping issues (which messes with the binding).
Here is an updated jsfiddle that you could use as a work around. Essentially, this example wraps another div around the items that you want to end up hiding. And then adds a next function so that the same scope is affected during the click that sets complete to true.
HTML:
<div ng-app="test">
<div ng-controller="TestCtrl">
<div ng-if="!complete">
<div>
<label for="testAmount">Amount:</label>
<select id="testAmount" ng-model="selectedItem" ng-options="item.name for item in items"></select>
</div>
<form role="form" name="testForm">
<div ng-repeat="i in getNumber(selectedItem.number) track by $index">
{{$index + 'hi'}}
</div>
<button class="btn btn-default" value="Next" title="Next" ng-click="next()">Next</button>
</form>
</div>
<div ng-if="complete">
</div>
</div>
</div>
JS:
angular.module('test', [])
.controller('TestCtrl', function($scope) {
$scope.complete = false;
$scope.items = [
{ name: '2', number: 2 },
{ name: '3', number: 3 },
{ name: '4', number: 4 }
];
$scope.selectedItem = $scope.items[0];
$scope.getNumber = function (number) {
return new Array(number);
};
$scope.next = function() {
$scope.complete = true;
};
})
I believe the problem is with your select statement inside an ng-if the selectedItem is never getting set. If you just don't want to show that dropdown when !complete change it to an ng-show and it works fine.
<div ng-show="!complete">
As to WHY the ng-model is not being bound inside the ng-if, I don't really know but it does make some sense in that you are trying to do a conditional bind which is a bit screwy

passing data to a directive for ng-repeat inside of the directive

I keep bumping my head on angular when I want to have a directive that displays a list of things, but I want it to be generic enough to handle more than one type/shape of object. For simple example lets say I have
<select ng-options="person.id by person in people" ng-model="selectPerson">
<option>
{{person.name}}
</option>
</select>
(Keep in mind that this is a simple example and something this simple would probably not benefit from being a directive)
Now I want to turn it into a generic directive called cool-select
I might try and do something like this in my js
//directive coolselect.directive.js
angular.module('mycoolmodule',[]).directive('coolSelect',function(){
return {
restrict:'E',
templateUrl:'js/coolselectdirective/_coolselect.html',//html from example above
scope:{
items:'=',
displayProperty:'#',
idProperty:'#',
selectedItem:'='
},
link:function(scope,element){
//do cool stuff in here
}
}
});
But then here is where I start to throw up a little in my mouth
//html template _coolselect.html
<select ng-model="selectedItem" ng-options="item[scope.idProperty] by item in items">
<option>
{{item[scope.displayProperty]}}
</option>
</select>
To be perfectly honest I am not even sure this would work in angular. I have seen what ui-select does by using an outer scope. Maybe that is the way to go?
https://github.com/angular-ui/ui-select/blob/master/dist/select.js#L892
But then I think I would need to get fancy with transclude, like ui-select does.
Isn't there an easier way? Am I trying to make directives to generic? Is this not a problem other people are running into?
EDIT:
In the end it would be ideal for it to look like this.
<cool-select repeat="person.id by person in people" display-property="name"></cool-select>
Please see demo below how to pass each object from array to directive in ng-repeater
var app = angular.module('app', []);
app.controller('homeCtrl', function($scope) {
$scope.people = [{
name: "John"
}, {
name: "Ted"
}]
});
app.directive('user', function() {
return {
restrict: 'EA',
template: "<p>*name:{{user.name}}</p>",
scope: {
user: '='
},
link: function(scope, element, attr) {
console.log(scope.user);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="homeCtrl">
<div ng-repeat="person in people" user="person"></div>
</div>
</div>
There are a few things to note here:
By placing ng-repeat on the select you are repeating the select.
The select element has a special attribute for repeating options called ng-options.
You don't need to refer to the scope in the template, just its properties. In an AngularJS template scope is implied, in fact that's the purpose of scope, to be the "scope" that you are accessing properties within.
You are not instantiating the coolSelect directive because you have restricted it to being used as an element but you are trying to use it as a class.
If you want to make a directive to simplify this process each time you want to repeat options or other components you will need to use syntax like item[displayProperty] in order to make it generic.
Why you need to build the options yourself with displayProperty in options tag ng-options can do more
<select
ng-model="myOption"
ng-options="value.id as value.label for value in myOptions">
<option value="">nothing selected Text</option>
</select>
value.id is the value stored in the ngModel myOption
value.label is the displayed label
<option value="">nothing selected Text</option>
for having a nothing selected option if necessary

How to make a preselection for a select list generated by AngularJS?

<select ng-model="team.captain" ng-options="player.name for player in team.players"></select>
This correctly creates a select list to choose the team captain. However, by default a blank option is selected. How can we preselect the first player from the list instead?
<select ng-model="team.captain" ng-options="player.name for player in team.players" class="ng-pristine ng-valid">
<option value="0">John</option>
<option value="1">Bobby</option>
</select>
I tried adding ng-init="team.captain='0'" but that didn't help.
Update Apparently this happens because
a value referenced by ng-model doesn't exist in a set of options passed to ng-options.
Source: Why does AngularJS include an empty option in select?
However, the question still remains why using ng-init doesn't work?
<select ng-init="team.captain='0'" ng-model="team.captain" ng-options="player.name for player in team.players"></select>
Here's what worked:
<select ng-init="team.captain=team.players[0]"
ng-model="team.captain"
ng-options="player.name for player in team.players"></select>
And what didn't work:
ng-init="team.captain='0'"
ng-init="team.captain='John'"
My guess is that Angular goes beyond simple comparison of values or labels. It probably compares object references.
Here's an alternate method for initializing a drop down menu using AngularJS.
(working example on JS Fiddle: http://jsfiddle.net/galeroy/100ho18L/1/)
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
</head>
<body ng-app="" ng-controller="myController">
<select
ng-model="carSelection"
ng-options = "x.make for x in cars">
</select>
<script>
function myController($scope) {
$scope.cars = [
{"make": "Nissan", "model": "Sentra"},
{"make": "Honda", "model": "Prelude"},
{"make": "Toyota", "model": "Prius"}
]
$scope.carSelection = $scope.cars[1]; // this line initializes the drop down menu
}
</script>
</body>
As #camus already mentioned in a comment, you need to set the model to a valid "label" value (or reference), not an index value. This is a bit odd since you can see an index value being used in the HTML.
Angular sets the value attributes in the HTML as follows:
when using array as datasource, it will be the index of array element in each iteration;
when using object as datasource, it will be the property name in each iteration.
When an item is selected, Angular looks up the correct entry in the array/object based on the index or property name.
What about ng-selected, as seen in this jsfiddle:
<select ng-model="val">
<option ng-repeat="opt in options" ng-selected="$first">
{{ opt }}
</option>
</select>
I achieved this using ng-model and ng-options.Basically your model and ng-options should be in sync.
When my model (projIndustry in this case) was initialized to some number then I had to use ind.ID in ng-options.(ID comparison).
When my model was initialized to object ,then I had to use ind(object) in ng-options.(Object comparison)
<select data-ng-options="ind.ID as ind.Display_Text for ind in arrIndustries"
data-ng-model="projIndustry" ng-change="onIndChange()" />
In case you are using angular 2+, here is what worked.
`
<mat-select matNativeControl placeholder="Select Item" [(ngModel)]="selectedItem" name="selectedCaseStudyName" (selectionChange)="onSelectionChangeHandler($event)" [ngStyle]="{'background-color':'silver'}">
<mat-option *ngFor="let item of myItemList" [value]="item.ItemName">
{{item.ItemName}}
</mat-option>
</mat-select>`
In .ts,
'selectedItem = myItemList[2].ItemName'
myItemList is list of objects with one of the fields being ItemName.
Important point is to bind [(ngModel)] to variable selectedItem and then initialize it with the required index of the list.
Whenever you bind [(ngModule)], make sure that you import NgModule
import {ngModule} from '#angular/core';

Categories