ng-repeat and polymorphic directive - angularjs

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)

Related

Directive scope variable not working with ng-show

I have a "home" controller that uses a directive. I want to be able to show/hide a button in the directive template depending on the value of a variable in the controller.
This is what my directive looks like:
angular.module("goleadoresApp").directive("golNavbar", [golNavbar]);
function golNavbar() {
return {
restrict: "EA",
templateUrl: "views/templates/golNavbar.html",
scope: {
pageTitle: "#",
showLockIcon: "#"
}
};
}
And my template:
<div class="bar bar-header bar-balanced">
<h1 class="title">
<i class="gol-logo-icon icon ion-ios-football"></i>
<span ng-bind="pageTitle"></span>
</h1>
<!--For testing purposes: This variable renders as false, however, ng-show in line below doesn't work-->
{{showLockIcon}}
<!--ng-show won't work. Element would still be visible even if showLockIcon is false-->
<a class="button icon ion-locked pull-right second-right-button"
ng-show="showLockIcon"></a>
<a class="button icon ion-help-circled" href="#/help"></a>
</div>
From my view, I'll use the template and pass it a controller variable (canPredictionsBePosted) that holds a value of true or false, and for which I expect that, if true, button will show in the template:
<gol-navbar page-title="{{matchWeek}}"
show-lock-icon="{{canPredictionsBePosted}}"">
</gol-navbar>
When running this code, I can actually see that the value passed to showLockIcon template variable is correct, but the element in the directive template, which has the ng-show, won't work. That means that when I pass it a value of false, the button is still visible.
Any ideas?
Furthermore, if I change the value of the canPredictionsBePosted variable in my home controller, the value of showLockIcon in the template doesn't get updated. How could I get this to work?
Thanks in advance, I'm new to directives, so all help is appreciated.
You need to make two changes.
1) Use = for showLockIcon like below:
scope: {
pageTitle: "#",
showLockIcon: "="
}
2) Remove the curly braces around canPredictionsBePosted in show-lock-icon="{{canPredictionsBePosted}}"
<gol-navbar page-title="{{matchWeek}}"
show-lock-icon="canPredictionsBePosted">
</gol-navbar>

Nested ng-repeat is never called

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">

Angularjs compile a directive inside ng-repeat with isolated scope

I have a directive in the form of a dropdown, pretty simple. The user can click a button to add as many as they need to in a ul, make their selections, and save it off. This is all inside of several ng-repeats.
I'm having trouble mastering the scope. As I expected, this works:
<div ng-repeat="group in groups" question-group="group" class="question-group">
<div ng-repeat="question in questions">
<ul>
<li ng-repeat="case in question.cases"></li>
<li><new-case group='group'></new-case></li>
</ul>
</div>
</div>
When I say "works", I mean that group is properly scoped (the data of the entire group is necessary for the resulting input).
When I switch it to "click to add":
<div ng-repeat="group in groups" question-group="group" class="question-group">
<div ng-repeat="question in questions">
<ul>
<li ng-repeat="case in question.cases"></li>
<li>add case</li>
</ul>
</div>
</div>
group is undefined in the scope. Here is my createNewCase function:
function createNewCase($event) {
var thisLi = angular.element($event.target).closest('li');
var listItem = $compile('<li><new-case group=\'group\'></new-case></li>');
var html = listItem($scope);
thisLi.before(html);
}
$scope.createNewCase = createNewCase;
And the newCase directive:
angular.module('groups.directives.newCaseDirective', [])
.directive('newCase', ['$window', function() {
return {
restrict: 'EA',
scope: { group: '=' },
templateUrl: 'groups/views/newcase.tpl.html'
};
}]);
I've been reading for days and I've tried a few other derivatives but I'm ultimately just not getting it. Help is greatly appreciated.
Thanks!
The issue is that group is created by ng-repeat and is only available in child scopes of ng-repeat.
Each repeated element is in it's own child scope. So your directive version works but your other one doesn't because the controller doesn't see those child scopes.
You would have to pass group as argument of the function if you want to access it in controller
<a href="#" ng-click="createNewCase($event, group)">

Not getting breadcrumb name in view

<td><i class="dataentry-breadcrumb" /></td>
<!-- {{breadcrumb.length}} {{breadcrumb[0].nodeName}} -->
<td ng-repeat="breadCrumbObj in breadcrumb">
<i class="dataentry-breadcrumbArrow" />
<a class="dataentry-breadcrumbElement" href='#'
title="{{breadCrumbObj.nodeName}}"
ng-click=updateBreadCrumb(breadCrumbObj.level)>{{breadCrumbObj.nodeName}}</a>
</td>
In the above code, nodeName in anchor tag is not getting display in UI. But it is present in the array.I tried to print it and commented. It was showing nodeName. But not in anchor tag. Please anyone help me...
I will show how I did it, as it seemed more logical and more apstracted so you can have better control of the views you are displaying.
First of all, you place it in the basic html file where it is displayed. Something like this:
<div ncy-breadcrumb></div>
Now this allows you to create a independent element for a breadcrumb, so now we have basically a custom directive for it, and within breadcrumb.html you model it with something like this, again mostly html.
<div class="breadcrumb">
<span ng-repeat="step in steps | limitTo:(steps.length-1)">
<a class="btn btn-tab" href="{{step.ncyBreadcrumbLink}}">{{step.ncyBreadcrumbLabel}}</a>
<span class="btn btn-tab btn-tab-divider">></span>
</span>
<span ng-repeat="step in steps | limitTo:-1">
<span class="btn btn-tab btn-current">{{step.ncyBreadcrumbLabel}}</span>
</span>
</div>
So now that you have some layout in the breadcrumb, you call it from the state controller where you control the views of your app. You first set it up as dependency.
'$breadcrumbProvider', function($breadcrumbProvider, ..) {
configure it within the function
// configure breadcrump
$breadcrumbProvider.setOptions({
templateUrl: 'scripts/nav/breadcrump.html'
});
and than all there is left to do, name how you call it depending on where you are in the app. Like this:
.state('root.vacancy', {
url: '/vacancies/:id',
shouldHighlight: 'home',
views: {
'main-content#root': {
templateUrl: 'scripts/vacancies/vacancy.html',
controller: 'VacancyController',
resolve: {
vacancy: function(vacancyService, $stateParams) {
return vacancyService.getVacancy($stateParams.id);
}
}
}
},
ncyBreadcrumb: {
label: "{{newProfile.vacancyTitle}}",
parent: 'root.home'
}
})
You set the state for routing for each state you have in the app. This way a solid amount of modular architecture is incorporated, and your breadcrumb is superbly independent :)
Hope it helps.

Creating a unique ID with a repetitive directive structure in AngularJS

So this is a bit complex, and I will try to explain it thoroughly. Ultimately the question I am trying to answer is, "How can I print the model, and not the value of the model, in the HTML?"
I believe the answer to that will solve my problem, but I'm going to discuss my problem in full so you all can tell me that I'm probably doing it wrong. In which case I have a lot of re-writing to do, so I hope not.
Scenario:
Here is the initial HTML page, where-in 'field' is a custom directive.
<field model="model1" placeholder="Model 1" type="input"></field>
<field model="model2" placeholder="Model 2" type="input"></field>
<field model="model3" placeholder="Model 3" type="input"></field>
Here is the custom directive.
app.directive("Field", function(){
return{
restrict: "E",
scope: { model: '=', placeholder:'#', type: '#'},
templateUrl: function(tElement, tAttrs){
return 'views/common/' + tAttrs.type + '.html';
}
};
});
Here is the Template for the directive. The type attribute just feeds in different templates, this way I don't have to do a complicated if then structure. Bear with me, it's might start to get a bit complex.
<field-label model="model">{{placeholder}}</field-label>
<div class="col-sm-2 no-right-pad">
<input type="text" ng-model="model.value" placeholder="{{placeholder}}" class="form-control input-sm"></input>
</div>
<div class="col-sm-2 no-pad">
<field-options field-options-model="model" class="field-options"></field-options>
</div>
As you should be able to tell, this is a form. Each initial directive call creates a form group containing a label, an input, and a directive with which options can be added to the model. For the sake of this exercise, lets say I want to be able to add a comment to each input. I get a little dirty when you go down into the field-options directive, and I change the model name, it's legacy from previous attempts and isn't hurting anything but I still might change that. I don't necessarily need it because the directives are nested and thus, share $scopes. Here's that directive:
app.directive("FieldOptions", function() {
return {
restrict: "E",
scope: { field: '=FieldOptionsModel' },
templateUrl: 'views/common/field-options.html'
};
});
And that template:
<div class="dropdown">
<a ng-href class="dropdown-toggle btn btn-bars" data-toggle="dropdown" tabindex="-1"><i class="fa fa-bars"></i></a>
<ul class="dropdown-menu pull-right">
<li><a data-toggle="modal" href="#{{field}}-comment-modal">Add Comment</a></li>
</ul>
</div>
<div id="{{field}}-comment-modal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3>Submit Comment</h3>
</div>
<div class="modal-body">
<div class="form-group">
<label class="control-label">Add Comment</label>
<textarea ng-model="field.comments[0].comment" class="form-control"></textarea>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" data-dismiss="modal">Cancel</button>
<button class="btn btn-primary" data-dismiss="modal" href="#">Add Comment</button>
</div>
</div>
</div>
</div>
OK, In case you didn't know, my field-options is a button with a dropdown that chooses a popup. Because we have repeating directives, you can tell instantly that there are essentially 3 identical copies of this popup being stored in the DOM structure right now. 1 for Model1, 1 for Model2, and 1 for Model3. We now have an issue with being able to call up the proper popup for each field group. Since it's built with the same ID usually, there are 3 identical ID's and browsers can't handle that so they only open the first. My solution, as you can see here, is to call the model name and inject it into the ID for the comment popup, thus making it unique per each field group. The problem is that id="{{field}}-comment-modal" does not print out id="model1-comment-modal" like I want it to, it instead prints out id="{"value" = "whatever the value might be set to, or blank if we click to add a comment before we fill out the input."}-comment-modal".
I return to my original question, "How can I print the model string, not the data it represents?" or, what am I doing wrong, or how to stop worrying and get a new job.
Note: Please be gentile, my example is dumbed down, actual use is way more complex and repetitive.
Not sure it will be possible to output the variable name but your issue can be resolve with one of the following solutions :
Add a 'name' property to your model object and use it to generate the ID or
Pass a new parameter called 'name' to the first diretive and propagate it to the inner one. Anyway you already use standard attributes such as placeholder and type or
Create a factory that will be in charge of generating unique IDs for the whole application or
Simply use a timestamp, Date.now(), to prefix your modal ID.
Hope this will help.
Update : After a bit of digging it may be possible to retrieve the string model1, model2 from the first directive by using the third attributes of the directive link function :
<field model="model1" placeholder="Model 1" type="input"></field>
angular.module('app', []).directive("field", function() {
return {
restrict: "E",
scope: { model: '=', placeholder:'#', type: '#'},
templateUrl: function(tElement, tAttrs){
return 'views/common/' + tAttrs.type + '.html';
},
link: function(scope, elem, attrs) {
console.log(attrs.model);
//Will output model1
}
}
});
By forwarding this string to the inner directive you may be able to generate unique IDs

Resources