How to get hold of template html in directive - angularjs

I'm trying to create a simple time picker directive. http://plnkr.co/edit/VYGqhPbHf1yqXLpemGEP
When user click on input field I want to display the content of my template html as a dropdown below the input (will take care of css later) so that user can select a time from the list. I'm not sure how to get hold of the template (something like angular.element(template).show())
Thanks!
Edit: I managed to come up with this: http://plnkr.co/edit/zAplNKVfohXLbIzwjhy4?p=preview
Everything works except when I click any of the list, it does not set the correct model value.

Try the following:
Embed the the HTML for the date picker list
Hide the list from the html
If the input gets focus change the visibility.
Pseudo code:
HTML:
<ul ng-show="listVisible">
<li> .... </li>
</ul>
JavaScript
scope.listVisible = false;
element.$on('focus', function() {
scope.listVisible = true;
});
Do something similar in reverse.

I managed to get it working. I had to create a new scope for the dropdown element. http://plnkr.co/edit/zAplNKVfohXLbIzwjhy4?p=preview

Related

Tag Manager Variable from a parent of click element (toggle)

Here is the structure of the toggle:
<div data-id="thisIsTheID" class="abc">
<div class="edf">
<button value="false"></button>
</div>
</div>
The trigger only works with click >> all elements >> click classes contains edf. I tried click element matches css selector
div.abc
div.abc *
div.abc button
but none will work as trigger.
However, I want the data-id as variable in Google Tag Manager in order to differentiate between different toggles.
How do I create a trigger that works and how do I get the variable?
Here the actual code and debugger view:
Try making a custom JS variable like so:
function(){
return {{Click Element}}.parentElement.getAttribute("data-test-id");
}
It should return the value of your attribute on click.
But the classes look auto-generated. They feel like they can and will change on cache refresh or on front-end builds. I would try to use selectors like div[data-test-id] * or even div[data-test-id=isAgePolicy] *

How do I dynamically style uib-accordion-group

I have created a uib-accordion in my Angular website and can get most of the functionality I want, with dynamic content changing accordingly.
I am having trouble styling the uib-accordion-group dynamically.
<uib-accordion-group panel-class="panel-danger">
<uib-accordion-heading>
Accordion Heading 1
Is fine and colours the whole heading Red/Pink, I want to change this to panel-warning or panel-info based on other variables on the page.
<uib-accordion-group panel-class="{{getPanelColor()}}">
<uib-accordion-heading>
Accordion Heading 1
The function seems to be called correctly and is triggered correctly with ng-click elsewhere.
I appears that I cannot change the value panel-class uses dynamically. So in this instance getPanelColor() returns 'panel-danger', 'panel-info' or 'panel-warning' depending on other variables. If I print this return value out on the page in another div or whatever it changes correctly. If I refresh the page the correct colours are displayed for the changed panel-group.
Is there another way of setting the color - I don't know what the classes are for the accordion-group. I have tried changing the color of a div withing the panel, but this is a child element and does not change the color of the whole heading.
Any help much appreciated. (I'll come up with a JSFiddle if the question is not clear)
If you look at the HTML after the panel-class has changed and Angular has digested the change, you will see this line:
<div class="panel panel-danger" ... panel-class="panel-default">
That is, there is a mismatch between class and panel-class (the former has panel-danger, whereas the latter has panel-default). The uib-accordion-group directive simply does not handle the change in the wanted manner.
One workaround is to add ng-if to the whole group:
<uib-accordion-group ng-if="render" panel-class="{{getPanelColor()}}">
... and just before you want to change panel-class, remove the whole element temporarily, so that Angular re-renders it from scratch. Hopefully, the following code explains the principle:
$scope.render = true;
$scope.panelColor = 'panel-danger';
$scope.setPanelColor = function(val) {
$scope.panelColor = val;
$scope.render = false;
$timeout(function () {
$scope.render = true;
});
};
$scope.getPanelColor = function() {
return $scope.panelColor;
};
See the proposal in action: http://plnkr.co/edit/XfJiPnNi1z4F9cgIVxxw?p=preview. Press 'Clear panel color OK'.
The downside is that the removal of the element causes some flickering.
I have added another button 'Clear panel color FAIL' that shows what happens in your failing case. Here is what the HTML looks like after you press the button, notice the mismatch panel-danger vs. panel-default:
Use an interpolated expression in the class attribute, for example:
class="{{!ctrl.valid?'notValid':'valid'}}"

Angular click for new page with infomation from clicked item JSON

I have a product page, and there is an option for a 'quick view' which opens the product description and images in a modal. I also have a 'full details' button where I would like the user to be taken to a new URL based on the item code, but also display the information from my JSON for that clicked product. This is what I have:
<a ng-click="store.selected = product" ng-href="{{store.selected.code}}.html" class="btn btn-sm">Full details</a>
Everything works great for the Modal, so I am presuming there is a problem moving onto a different page and carrying this information across? I have mocked it up in Plunker to show the problem:
Plunker here
Any help would be much appreciated.
You should consider using ng-route to switch the view to a different html file. But you can also use ng-include with a little less set up.
When the Full details link is clicked, toggle a variable that tracks whether the gallery or the details should be visible. And, construct the path to the html template you want to display:
$scope.setSelection = function(product) {
$scope.store.selected = product;
$scope.detail.show = !$scope.detail.show;
$scope.detail.source = product.code + '.html'
}
Then, use ng-include to display the template:
<div ng-if="detail.show" ng-include="detail.source">
</div>
Here is an update of your plunker: http://plnkr.co/edit/zhsY6TAjONewyInGiwUC?p=preview
Note that the template should just be a snippet of html.. you don't need to include scripts like angular again.

ng-show not showing/hiding HTML List after variable value change

I have recently started building some functionality to restrict what a user sees in the UI based on their rights in the system.
When a user visits the site for the first time, there is a list item containing an administration menu that is hidden by default. When the user logs in successfully, the list item should now be shown if the user has the correct user rights. Here is the code of the li:
<li ng-show="isAdministrator">
... items here
</li>
On my controller I have the following code:
$scope.isAdministrator = authService.isUserAdmin;
At this point, if I console.log the authService.isUserAdmin value it is set to true, but the list item still does not show. If I manually refresh the page then the list item is shown. The reverse happens when I log out. Once I have logged out, the authService.isUserAdmin is set to false but the list item still shows until I refresh the page.
I can only assume that I am missing something very basic but I cannot find the problem. Just a quick side note, the code for the list is within a div that has the ng-controller set to the correct controller and the list has not yet been refactored into a directive.
Any help would be appreciated
i am sure that $scope.isAdministrator = authService.isUserAdmin execute before the authService.isUserAdmin executes
use this code:
in html:
<li ng-show="ser.isUserAdmin">
... items here
</li>
in controller:
$scope.ser= authService;
Perhaps this has to do with authService.isUserAdmin being a primitive type. Try this:
controller:
$scope.authServ = authService;
view:
<li ng-show="authServ.isUserAdmin">...</li>

What's a good way to control an angular-ui accordion programmatically?

I am using the accordion directive from http://angular-ui.github.com/bootstrap/ and I need to have more control over when the accordions open and close.
To be more precise I need a button inside the accordion-group that will close its parent accordion and open the next one (so basically mimic what clicking the next header would do if close-others was set to true).
I also need to do some validation before I can allow an accordion to be closed and the next one to be opened, and I also need to wire this up to click events on the accordion headers.
I am pretty new to angular and we're currently rewriting an application from Backbone+JQuery to Angular. In the Backbone-version we were using Twitter Bootstrap accordions and we were opening and closing them using JQuery. While we can still keep doing this I would rather get rid of JQuery DOM manipulation completely so I am looking for a pure angular solution to this.
What I've tried to do in terms of validation is
<accordion-group ng-click="close($event)">
and in my controller
event.preventDefault();
event.stopPropagation();
This obviously did not work as the DOM element is replaced by the directive and the click-handler is never added. I've been going over the source code (and found a few very nice undocumented features) but I'm at a loss over where to even begin solving this specific challenge. I was considering forking angular-ui and try to add this functionality to the accordion directive but if I can achieve this without modifying the directive that would be a lot nicer.
There is the is-open attribute on the accordion-group which points to a bindable expression. By using this expression you can control accordion items programatically, ex.:
<div ng-controller="AccordionDemoCtrl">
<accordion>
<accordion-group ng-repeat="group in groups" heading="{{group.title}}" is-open="group.open">
{{group.content}}
</accordion-group>
</accordion>
<button class="btn" ng-click="groups[0].open = !groups[0].open">Toggle first open</button>
<button class="btn" ng-click="groups[1].open = !groups[1].open">Toggle second open</button>
</div>
and the working plunk here: http://plnkr.co/edit/DepnVH?p=preview
For whoever the solution by #pkozlowski.opensource is not working (me for example) you could just force the component to accept the CSS that will close it (without transition that is).
The Theory: The angular directive gets expanded into standard HTML, div elements mainly, where the CSS styles give it the appearance of the accordion. The div with class .panel-collapse is the body of the accordion-group element. You can swap its second class from .in to .collapse along with a few other changes as seen below.
The Code:
$scope.toggleOpen = function(project) {
var id = '<The ID of the accordion-group you want to close>';
var elements = angular.element($document[0].querySelector('#'+id));
var children = elements.children();
for(var i = 0; i < children.length; i++) {
var child = angular.element(children[i]);
if(child.hasClass('panel-collapse')) {
if(child.hasClass('in')) { // it is open
child.removeClass('in');
child.addClass('collapse');
child.css('height', '0px');
} else { // it is closed
child.addClass('in');
child.removeClass('collapse');
child.css('height', 'auto');
}
}
}
};
As we are talking about Angular, it is very possible that you are generating the accordion through an ng-repeat tag. In this case you can also generate the id's for the elements like:
<accordion-group ng-repeat="user in users"
is-disabled="user.projects.length == 0"
id="USER{{user._id}}">
Given a Mongoose model User, notice that the id I am giving is not user._id but has 'USER' appended in front. This is because Mongoose might generate id's that start numerically and querySelector does not like that ;-) go figure!

Resources