So I read Why is ng-non-bindable required for <ui-gmap-windows> element in Angular Google Maps?
and I get how ng-non-bindable is used in the directive.
My problem is that I'm trying to use ng-if to conditionally reveal some icons in my marker infowindows. ng-if doesn't work with ng-non-bindable, and of course, the info windows don't work without it.
If someone can tell me how to make ng-if work in this situation or give an alternative solution, i'd greatly appreciate it.
<ui-gmap-windows show="show">
<div ng-non-bindable>{{obj.name}}<br>{{distance}} miles
<span class="ion-man" ng-if="obj.men"></span>
<span class="ion-woman" ng-if="obj.women"></span>
<span class="ion-ios-people" ng-if="obj.people"></span>
</div>
</div>
</ui-gmap-windows>
You need to use the templateUrl and templateParameter attributes on the ui-gmap-windows directive. The templateUrl is a property on the object that you're passing in which is a string that is the path to the .html template you want to use. The templateParameter is a property on the object that you are passing which is an object containing the parameters you want passed in. See code below. Also you can reference the docs here: http://angular-ui.github.io/angular-google-maps/#!/api/windows
<ui-gmap-windows idKey="'name'" show="show" templateUrl="'pathToHtmltemplate.html'" templateParameter="'{name: 'name', distance: 1.3, womens_restroom: true}'">
</ui-gmap-windows>
Then in your html template you can use ng-if no problem.
Note you have to reference the object you pass into templateParameter as parameter in the html template so the
pathToHtmlTemplate.html file would look like this
<div>
{{parameter.name}}<br>{{parameter.distance}} miles
<span ng-if="parameter.womens_restroom" class="ion-woman"></span>
</div>
The reason it does not work is because, ng-if (600) has lower priority than ng-non-bindable (1000) and ng-non-bindable is terminal:true so ng-if never compiles due to the terminal nature of ng-non-bindable. You could try using ng-switch (1200) directly on the element with ng-non-bindable though since it has higher priority but not sure if that is applicable in your case if you may have multiple conditions that can be true.
You can as well create your own directive say my-non-bindable anddefine it with configuration, {priority:599, terminal:true} and use it with ng-if say:
.directive('gmapTemplate', function() {
return {
priority: 599,
terminal: true
};
});
and use it with ng-if.
<div gmap-template ng-if="someCond">{{obj.name}}<br>{{distance}} miles
Otherwise you will have to wrap non bindables within element with ng-if
<div ng-if="someCond">
<span ng-non-bindable>{{obj.name}}<br>{{distance}} miles</span>
</div>
angular.module('app', []).directive('gmapNonBindable', function() {
return {
priority: 599,
terminal: true
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
<div ng-app="app" ng-init="test:123; show:false">
Show-->
<input type="checkbox" ng-model="show" />
<div gmap-Non-Bindable ng-if="show">{{test}}</div>
</div>
Related
I have an AngularJS directive meant to interact with notifications pushed from a server. The problem I am having is that currently the method I am using is to have an ng-repeat which keeps track of all the notifications during the session, and then displaying them as they are added to the array. What I want to happen, is that an alert comes, it's added to the DOM, it stays for a couple seconds, and removes itself from the DOM. At this point the issue is that the element will hide, but it will not remove itself from the DOM. Being that the element is absolutely positioned, after the fadeout is prevents me from accessing item that it is in front of. I assumed that element.remove() and the element.$destroy() would do the trick, but it seems like the element is not being from the ng-repeat or possibly that the $scope of the directive is not being deleted. Any help would be greatly appreciated.
angular.module('bmuApp').directive('messageNoti', function($timeout){
return {
scope: {
alert: '=messageNoti'
},
replace: true,
restrict: 'EA',
templateUrl: 'partials/authenticated/homepage/alerts.html',
link: function(scope, element, attrs) {
$timeout(function(){
element.addClass('fadeOut');
$timeout(function(){
element.remove();
}, 500);
}, 5000);
element.on('$destroy', function () {
scope.$destroy();
});
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!------------>
<!-- Alerts -->
<!------------>
<div class="alert-container" ng-cloak>
<a class="alert animated fadeInUp" ng-repeat="alert in alerts" ng-href="messages/{{ alert.user.thread_id }}" message-noti="alert">
</a>
</div>
<!-----Template for Alert------>
<div class="row">
<span class="close">
<i class="glyphicon glyphicon-remove"></i>
</span>
<div class="col-xs-12 text-left">
<span class="profile-image" style="background-image:url('https://www.blackmarketu.com/{{ ::alert.user['profile-picture'] }}');"></span>
<h5> {{ ::alert.user["user-firstname"] }} {{ ::alert.user["user-lastname"] }} </h5>
<p>{{ ::alert.message }}</p>
</div>
</div>
You don't need to interact with the DOM directly to remove it. Just splice (remove) that array member from the array.
So your logic is a bit wrong, because you have an array of alerts and you are not removing them, you just want to remove the DOM element.
If you don't want to change your code too much and add a parent directive to handle this, you can pass both "alert" (current array member) and "alerts" (entire array of alerts) to your directive and then in your timeout callback instead of removing the DOM element, you splice that array member from the array.
Remove replace: true. Since the ng-repeat directive creates inherited scopes and your directive creates isolate scope, they won't play well together on the same element.
From the Docs:
replace ([DEPRECATED!], will be removed in next major release - i.e. v2.0)
specify what the template should replace. Defaults to false.
true - the template will replace the directive's element.
false - the template will replace the contents of the directive's element.
-- AngularJS Comprehensive Directive API
From GitHub:
Caitp-- It's deprecated because there are known, very silly problems with replace: true, a number of which can't really be fixed in a reasonable fashion. If you're careful and avoid these problems, then more power to you, but for the benefit of new users, it's easier to just tell them "this will give you a headache, don't do it".
-- AngularJS Issue #7636
I am trying to initialize $scope.selectedModel to model1with ng-init. However, the following HTML fails to accomplish this:
<div class="productImage">
<div class="imageGallery" ng-init="selectedModel='model1'">
<div ng-repeat="mod in pTab" ng-if="modelIsActive(mod)">
<div ng-repeat="img in mod.galleryImages">
<img class="overviewProductImage" ng-src="{{img.image}}" ng-if="productImageIsActive(img, $index)"/>
</div>
</div>
</div>
</div>
And here's the modelIsActive method that uses selectedModel:
$scope.modelIsActive = function (tab) {
return tab.model== $scope.selectedModel;
}
Eventually I will want to use ng-init="selectedModel= mod.model" but when that didn't work I substituted the simple string 'model1' and found it still wasn't initializing selectedModelto that string.
How can I use ng-init to set $scope.selectedModel? Or should I be using something else? Do I need to use $watch or something similar?
If you can, it is better to initialize your selectedModel in your controller rather than in the HTML using ng-init: this directive can have exepected behaviors.
If you really need to though, you either need to at least define $scope.selectedModel in your controller and then set a value in the ng-init, or use an object instead of directly a value, such as
<div ng-init="model.selectedModel = 'model11'">
(but you'll still need to initialize $scope.model in your controller)
please put your ng-if into the inner of repeat:
<div ng-repeat="mod in pTab">
<div ng-repeat="img in mod.galleryImages" ng-if="modelIsActive(mod)">
In brief
I'm looking for a cleaner way to work around an issue that involves isolate scopes. I'm not sure there's a better workaround that what I have, but I hope so as I'm not too happy with it.
The demo
Demo on Plunkr
Contains both a directive that shows the problem, and another one with the dirty fix.
Change the values of the inputs and see it doesn't get propagated for one of them.
The story
I wrote a directive that contains a ng-switch. The code is:
angular.module('core')
.directive('otherSearchField', function() {
return {
templateUrl: 'otherSearchField.html',
restrict: 'E',
scope: {
field: '=',
placeholder: '#',
condition: '#searchWhen'
}
};
});
Its template is:
<section ng-switch="condition">
<div ng-switch-when="true">
<input type="text" ng-model="field" placeholder="{{placeholder}}">
<button ng-click="search()">Search</button>
</div>
<div ng-switch-default>
{{field}}
</div>
</section>
I could rewrite it as a ng-if, but what matters really is that in both cases, a new scope is created by ng-switch or ng-if.
I use the directive this way:
<div ng-controller="Ctrl">
<other-search-field field="query.city" placeholder="City" search-when="{{edition.city}}"></other-search-field>
</div>
The issue
As you notice, in the directive template, we have an input bound to "field". That one is bound to the calling template via the = notation in the directive definition.
However, because we're not using the object notation, entering something in the input modifies the field in the ng-switch's scope, but does not propagate out of it.
A (dirty) solution
My current solution is to use the object notation in the directive's template, which means I need to pass the containing object to the directive, and the name of the property I want to modify.
<section ng-switch="condition">
<div ng-switch-when="true">
<input type="text" ng-model="fieldParent[field]" placeholder="{{placeholder}}">
<button ng-click="search()">Search</button>
</div>
<div ng-switch-default>
{{fieldParent[field]}}
</div>
</section>
Usage:
<my-search-field field-parent="query" field="customer" placeholder="Customer" search-when="{{edition.customer}}"></my-search-field>
This works: I'm using the object notation so the changes on the input propagate all the way up (see on the Plunkr linked above).
So ?
Hmm, I'm not too happy to pass an entire object when only one of its properties is needed though. Is there a better way ?
NB
Note I could also, in this case, use multiple ng-show as they don't create their own scope. But I'm interested in the more general issue shown here, not in this specific case.
Thanks for reading all the way. Kudos to you !
In your otherSearchField.html template, you can access the $parent $scope's field property, thereby accessing the isolate scope that your directive <other-search-field> is using.
DEMO
<section ng-switch="condition">
<div ng-switch-when="true">
<input type="text" ng-model="$parent.field" placeholder="{{placeholder}}">
<button ng-click="search()">Search</button>
<div>value in the template, in "switch": {{$parent.field}}</div>
</div>
<div ng-switch-default>
{{field}}
</div>
</section>
<div>
value in the template, out of "switch": {{field}}
</div>
I am trying to conditionally display a directive based on a boolean value stored in the parent scope. I can't figure out why the below does not work. By, "not work" I mean neither directives are displayed.
<ul class="nav navbar-nav pull-right" ng-switch="userIsAuthenticated">
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in anonymousMenuItems" ng-switch-when="false"></account-item>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in authenticatedMenuItems" ng-switch-when="true"></account-item>
</ul>
Neither directives are shown even thought "userIsAuthenticated" is set to 'false' in my test case. If I add {{userIsAuthenticated}} above the directives 'false' is output as expected.
I've also tried this:
<ul class="nav navbar-nav pull-right" ng-switch={{userIsAuthenticated}}>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in anonymousMenuItems" ng-switch-when={{false}}></account-item>
<account-item item="accountMenuItem" ng-repeat="accountMenuItem in authenticatedMenuItems" ng-switch-when={{true}}></account-item>
</ul>
If I remove the conditional ng-switch-when attribute from either of the directives they will display. So I'm know the problem is my ng-switch.
Your usage of ng-switch works in this simplified demo, of course without your account-item directive:
http://plnkr.co/AppN8xmFeIwjaP631lj7
Without seeing the code for account-item, it is hard to guess what might be interfering with it. You might consider using ng-if to handle displaying one item or another.
<ul>
<div ng-if="!userIsAuthenticated">Content when not authenticated</div>
<div ng-if="userIsAuthenticated">Content when authenticated</div>
</ul>
Update
Also make sure you bind to an object property, instead of a primitive boolean. Like: user. authenticated
Since ngSwitchWhen has a priority of 800, you need to set a higher priority to your custom directive (i.e. account-item) in order for it to be compiled before being process by the ngSwitchWhen directive. E.g.:
.directive('accountItem', function () {
return {
...
priority: 900,
...
};
});
I've created a single answer survey, and I want to translate it into a directive. I'ts a single answer because only one answer can be chosen at the same time, and it triggers x or y function (also ng-class) depending on the chosen answer.
I have the working functionality without directive in this jsfiddle. As you'll see, every link triggers an different ng-click.
<div class="col-md-4">
<a class="btn col-md-12" ng-click="continent='1'" ng-class="{'active' : continent == '1'}">North America</a>
</div>
<div class="col-md-4">
<a class="btn col-md-12" ng-click="continent='2'" ng-class="{'active' : continent == '2'}">South America</a>
</div>
My problem is that this method doesn't work (or better, I don't know how to do it) when translated into a directive. You can see the jsfiddle with the directive here.
And the js:
.directive('myContinent', function() {
return{
restrict: 'E',
template: '<div class="col-md-4"> \
<a class="btn col-md-12" ng-click="continent=\'1\'" ng-class="{active : continent == \'1\'}">{{text}} \
</a> \
</div>',
replace: true,
scope: {
text: '#'
}
};
});
I think one of the easiest and clean ways is to pass the controller's scope variable to your directive scope (using =). You can save the selected continent in this variable and use it in multiple directive instances.
Fiddle: http://jsfiddle.net/pascalockert/xRy7H/1/
The problem is that all your my-continent directives has different isolated scopes, so each of them has scope.continent == 1 after you click it.
You can modify your directive in any of the available solutions:
Build in on top of radio input group and standard angular form.
Use $parent.continent and assign different values to it (bad practice actually).
Use a single directive with transclusion (where options are transcluded into the 'parent' directive).
I'll leave the choice for you, as it depends on the overall architecture of your decision.