Nested ng-repeat is never called - angularjs

I'm new in angular and I'm trying to access some datas inside a variable in which there is tab nested in another tab. (I can't link it because it's sensitive). Anyway, I'm trying to use a nested ng-repeat to access it, but the second ng-repeat is never called, and my console doesn't give me a single error message. This is basically how it looks.
dico = [
{ ...
content : []
},
{ ...
content : []
},
...
];
After checking existing questions (https://stackoverflow.com/questions/19206760/angular-nested-ng-repeat-failure#= & passing 2 $index values within nested ng-repeat) I don't get why my part isn't working.
<span ng-repeat="toolbar in dico">
<candidate-toolbar-review average="toolbar.average" labeltoolbar="{{toolbar.name}}">
<span ng-repeat="field in toolbar.content" layout="row" layout-wrap layout-align="start center">
<candidate-field-review labelfield="{{field.name}}" ng-model="field.value" my-model="field.checked">
</candidate-field-review>
</span>
</candidate-toolbar-review>
</span>
In this sample of code, both candidate-field-review and candidate-toolbar-review are directives, if I comment the first one, the second one will output correctly. Elsewise, the first one is printed as expected.
I tried to use $parent and track by $index, but I don't really understand how those work. I also tried to use div instead of span or to not use span at all (and include the ng-repeats inside the candidate-toolbar and candidate-field). What am I missing here ? Thanks !
EDIT :
I used those lines instead and it worked out. I still don't really know why it wouldn't work the other way around though.
<span ng-repeat="toolbar in dico">
<candidate-toolbar-review ng-model="toolbar.average" labeltoolbar="{{toolbar.name}}" ng-click="showSmart=!showSmart">
</candidate-toolbar-review>
<span ng-repeat="field in toolbar.content">
<candidate-field-review labelfield="{{field.name}}" ng-model="field.value" my-model="field.checked">
</candidate-field-review>
</span>
</span>

I believe that
<span ng-repeat="field in toolbar.content" layout="row" layout-wrap layout-align="start center">
is never called because toolbar exists only in <span ng-repeat="toolbar in dico"> scope.
candidate-toolbar-review has it's own controller.
to pass toolbar to candidate-toolbar-review you need todo something like:
.directive('candidateToolbarReview', function() {
return {
restrict: 'E',
transclude: true,
scope: {
toolbar : '#'
},
and:
<candidate-toolbar-review toolbar="toolbar" average="toolbar.average" labeltoolbar="{{toolbar.name}}">
<span ng-repeat="field in toolbar.content" layout="row" layout-wrap layout-align="start center">

Related

ng-repeat and polymorphic directive

I'm wondering if there is a way to build a custom Element directive to use with ng-repeat in this way:
<div class="list">
<my-custom-directive class="item item-icon-right" ng-repeat="a in concepts">
<p>{{::a.label}}</p>
<i class="icon ion-forward muted"></i>
</my-custom-directive>
</div>
my-custom-directive should compile itself in an anchor if a.href exists, in a paragraph if it doesn't.
The problem basically is merely design: I have some items which doesn't have an href, but they should still be in the list. In Ionic1 it looks like I can do a div list or an a list, but not mixing up without breaking the list design..
Sure you can. Something like this:
<my-custom-dir ng-repeat="a in concepts"></my-custom-dir>
where, directive is like,
app.directive('myCustomDir', function() {
return {
restrict: 'E',
templateUrl: 'custom.html'
}
})
And, the custom.html template,
<p ng-hide="a.href">{{::a.label}}</p>
<a ng-href="{{a.href}}" ng-show="a.href">{{::a.label}}</a>
<i class="icon ion-forward muted"></i>
Also, I kept the $scope.concepts to have dummy object as follows,
$scope.concepts = [{
href: 'eample.com',
label: 'example'
}, {
label: 'example1'
}, {
href: 'eample2.com',
label: 'example2'
}]
working example
Although, I think you should be able to have a div.item with ng-repeat inside your .list. And in the div.item you should be able to have whatever you want (not sure how ionic1 deals with that though)

using a click listen in angular inside ng-repeat

how do I keep this function from firing/incrementing every single div generated? I only want it to upvote the specific one that I click on. I think the problem is that is inside the ng-repeat.
<div class="text-center" ng-repeat="value in unique">
<span ng-click="incrementUpvotes()">
{{value[0]}} and {{value[1]}} for {{value[2]}} and {{value[3]}} upvotes: {{upvotes}}
</span>
</div>
What I would do is have the function in your controller handle the incrementing of the upvotes, not the actual DOM. Then you can pass in which value you want to be incremented through that function. For example your controller could look like:
function incrementUpvotes(valueNumber){
$scope.value[valueNumber]++;
}
and your HTML like:
<div class="text-center" ng-repeat="value in unique">
<span ng-click="incrementUpvotes(value)">{{value[0]}} and {{value[1]}} for {{value[2]}} and {{value[3]}} upvotes: {{upvotes}}</span>
</div>

ng-class with scope variable

<div ng-repeat="app in fields track by $index">
<span class="{{app.icon}}"></span>
<span ng-class="app.icon"></span>
</div>
var originalFields = [
{title: 'Text Field', icon: 'fa-pencil'},
{title: 'Multiple Choice'}
];
These classes are working correctly the first time
When i used something like this $scope.fields = angular.copy(originalFields);
Then it works sometimes, but doesn't work sometimes I get class="app.icon" instead of class="fa-edit"
I even tried using $scope.$apply(); after copying, but it doesn't work
value in app.icon = "fa-edit"
What am i doing wrong?
<div ng-repeat="app in fields">
<span class="{{app.icon}}"></span>
<span ng-class="app.icon"></span>
</div>
I have removed the track by $index It is working fine.
When First element got deleted it is copying second element's properties when using track by $index, My first element icon property is being overridden by second element. Second element doesn't have icon property.
Strange!!

Angular.js ng-switch-when not working with dynamic data?

I'm trying to get Angular to generate a CSS slider based on my data. I know that the data is there and am able to generate it for the buttons, but the code won't populate the ng-switch-when for some reason. When I inspect the code, I see this twice (which I know to be correct as I only have two items):
<div ng-repeat="assignment in assignments" ng-animate="'animate'" class="ng-scope">
<!-- ngSwitchWhen: {{assignment.id}} -->
</div>
My actual code:
<div ng-init="thisAssignment='one'">
<div class="btn-group assignments" style="display: block; text-align: center; margin-bottom: 10px">
<span ng-repeat="assignment in assignments">
<button ng-click="thisAssignment = '{{assignment.id}}'" class="btn btn-primary">{{assignment.num}}</button>
</span>
</div>
<div class="well" style="height: 170px;">
<div ng-switch="thisAssignment">
<div class="assignments">
<div ng-repeat="assignment in assignments" ng-animate="'animate'">
<div ng-switch-when='{{assignment.id}}' class="my-switch-animation">
<h2>{{assignment.name}}</h2>
<p>{{assignment.text}}</p>
</div>
</div>
</div>
</div>
</div>
EDIT: This is what I'm trying to emulate, though with dynamic data. http://plnkr.co/edit/WUCyCN68tDR1YzNnCWyS?p=preview
From the docs —
Be aware that the attribute values to match against cannot be expressions. They are
interpreted as literal string values to match against. For example, ng-switch-when="someVal"
will match against the string "someVal" not against the value of the expression
$scope.someVal.
So in other words, ng-switch is for hardcoding conditions in your templates.
You would use it like so:
<div class="assignments">
<div ng-repeat="assignment in assignments" ng-animate="'animate'">
<div ng-switch="assignment.id">
<div ng-switch-when='1' class="my-switch-animation">
<h2>{{assignment.name}}</h2>
<p>{{assignment.text}}</p>
</div>
</div>
</div>
Now this might not fit your use case exactly, so it's possible you'll have to rethink your strategy.
Ng-If is probably what you need — also, you need to be aware of "isolated" scopes. Basically when you use certain directives, like ng-repeat, you create new scopes which are isolated from their parents. So if you change thisAssignmentinside a repeater, you're actually changing the variable inside that specific repeat block and not the whole controller.
Here's a demo of what you're going for.
Notice I assign the selected property to the things array (it's just an object).
Update 12/12/14: Adding a new block of code to clarify the use of ng-switch. The code example above should be considered what not to do.
As I mentioned in my comment. Switch should be thought about exactly like a JavaScript switch. It's for hardcoded switching logic. So for instance in my example posts, there are only going to be a few types of posts. You should know a head of time the types of values you are going to be switching on.
<div ng-repeat="post in posts">
<div ng-switch on="post.type">
<!-- post.type === 'image' -->
<div ng-switch-when="image" class="post post-image">
<img ng-src="{{ post.image }} />
<div ng-bind="post.content"></div>
</div>
<!-- post.type === 'video' -->
<div ng-switch-when="video" class="post post-video">
<video ng-src="{{ post.video }} />
<div ng-bind="post.content"></div>
</div>
<!-- when above doesn't match -->
<div ng-switch-default class="post">
<div ng-bind="post.content"></div>
</div>
</div>
</div>
You could implement this same functionality with ng-if, it's your job to decide what makes sense within your application. In this case the latter is much more succinct, but also more complicated, and you could see it getting much more hairy if the template were any more complex. Basic distinction is ng-switch is declarative, ng-if is imperative.
<div ng-repeat="post in posts">
<div class="post" ng-class="{
'post-image': post.type === 'image',
'post-video': post.type === 'video'">
<video ng-if="post.type === 'video'" ng-src="post.video" />
<img ng-if="post.type === 'image'" ng-src="post.image" />
<div ng-bind="post.content" />
</div>
</div>
Jon is definitely right on. Angular does not support dynamic ngSwitchWhen values. But I wanted it to. I found it actually exceptionally simple to use my own directive in place of ngSwitchWhen. Not only does it support dynamic values but it supports multiple values for each statement (similar to JS switch fall-throughs).
One caveat, it only evaluates the expression once upon compile time, so you must return the correct value immediately. For my purposes this was fine as I was wanting to use constants defined elsewhere in the application. It could probably be modified to dynamically re-evaluate the expressions but that would require more testing with ngSwitch.
I am use angular 1.3.15 but I ran a quick test with angular 1.4.7 and it worked fine there as well.
Plunker Demo
The Code
module.directive('jjSwitchWhen', function() {
// Exact same definition as ngSwitchWhen except for the link fn
return {
// Same as ngSwitchWhen
priority: 1200,
transclude: 'element',
require: '^ngSwitch',
link: function(scope, element, attrs, ctrl, $transclude) {
var caseStms = scope.$eval(attrs.jjSwitchWhen);
caseStms = angular.isArray(caseStms) ? caseStms : [caseStms];
angular.forEach(caseStms, function(caseStm) {
caseStm = '!' + caseStm;
ctrl.cases[caseStm] = ctrl.cases[caseStm] || [];
ctrl.cases[caseStm].push({ transclude: $transclude, element: element });
});
}
};
});
Usage
Controller
$scope.types = {
audio: '.mp3',
video: ['.mp4', '.gif'],
image: ['.jpg', '.png', '.gif'] // Can have multiple matching cases (.gif)
};
Template
<div ng-switch="mediaType">
<div jj-switch-when="types.audio">Audio</div>
<div jj-switch-when="types.video">Video</div>
<div jj-switch-when="types.image">Image</div>
<!-- Even works with ngSwitchWhen -->
<div ng-switch-when=".docx">Document</div>
<div ng-switch-default>Invalid Type</div>
<div>

Directive that creates child scope in AngularJS

When creating complex forms I found the need of separating some parts of my view into different child scopes to be able to have individual visual properties.
The good example could be implementing 'click-to-edit' behaviour: when you have one html to view something and another to edit.
One of the solution is to create en directive that will have isolated scope. But in case if html markup for different properties differs a lot, you need to have kind of "double transclusion" (manually compile templates upon switching).
So more simplier is to have some small copy-pasting, but show dirrectly what is going on with view. This simplifies markup a lot.
Here is a sample code that illustrates that problem:
<span class="editable" >
<span ng-hide="editing">
{{user.first}} <span ng-click="editing = true"><i class="icon-pencil"></i></span>
</span>
<span ng-show="editing">
<input type="text" ng-model="user.first">
<span ng-click="editing = false"><i class="icon-ok"></i></span>
</span>
</span>
<span class="editable" >
<span ng-hide="editing">
{{user.last}} <span ng-click="editing = true"><i class="icon-pencil"></i></span>
</span>
<span ng-show="editing">
<input type="text" ng-model="user.last">
<span ng-click="editing = false"><i class="icon-ok"></i></span>
</span>
</span>
In this scenario 'child scopes' is first that come into mind.
But I didn't found directive that simply creates new scope in AngularJS. Is there a one?
As one of the very straight solution I've wrote an simple one-line directive:
.directive('childScope', function() {
return { scope: true, restrict:'AE' }
});
And use it just adding to <span class="editable" child-scope> in my source example.
But may be there is some standard directive for doing that?
If not, I consider this solution could be usefull for others.

Resources