Keep track of bootstrap's component state in Angularjs - angularjs

I'm learning Angular JS.
I have my angularjs component whose HTML template consists of Bootstrap's Dropdown component plus a message <div>:
<div class="dropdown-container">
<div id="messageDiv"></div>
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">
Dropdown
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li>Action</li>
<li>Another action</li>
<li>Something else here</li>
</ul>
</div>
</div>
This is straight from Bootstrap: http://getbootstrap.com/components/#dropdowns
I would like my #messageDiv to have text "Dropdown is open" when the dropdown is open and "Dropdown is closed" when dropdown is closed. How can I achieve this "the Angular way"?

I strongly recommend you to not reinvent the wheel and use the Angular UI Bootstrap.There you can find the dropdown directive. Then u can manage state of the dropdown directive using isOpen property binding and show/hide #messageDiv based on that property.

You can track if the dropdown opens and closes with events. Bootstrap provides a javascript file that you need to include and it also requires jquery. I made a working Plunker here: https://plnkr.co/edit/pE7bMI?p=preview
Once you include jquery, and bootstrap's js file you can track the events like this:
$scope.dropdownOpen = false;
// get a reference to your dropdown parent (in your case the <div class="dropdown>" line)
var dropdown = angular.element('#myDropdown');
dropdown.on('show.bs.dropdown', function(event) {
$scope.dropdownOpen = true;
$scope.$evalAsync();
});
dropdown.on('hide.bs.dropdown', function(event) {
$scope.dropdownOpen = false;
$scope.$evalAsync();
});
Why is $scope.evalAsync() in there?
Since angular.element is an alias for jqlite or jquery we are actually binding outside of angular. It doesn't know about the change so we need to tell the scope that something has changed.

Related

Is it possible to change angular bootstrap uib-dropdown templateUrl dynamically?

I want to change the uib-dropdown template dynamically when the user clicks one of its <li>, just like he could "navigate" within that dropdown.
I tried to make it via templateUrl, but nor the ng-templates nor standalone partials can successfully change the dropdown template dynamically, just like this plunkr demonstrates.
My goal is to create a faceted navigation via this dropdown to build query visually, as seen on Fieldbook's Sprint tracker (account required), which is something really like Pure Angular Advanced Searchbox, but I'm having overwhelming issues using this library.
Is this possible to achieve using just AngularJS and angular-bootstrap?
EDIT: You should assign the value of template-url using a controller var which changes as the user select any of the options, then you "repaint" the component, this way the "new" dropdown is "repainted" with the new template.
Yes, it's possible according to the official documentation, though I've never done this before.
You can specify a uib-dropdown-menu settings called template-url.
According to the docs the
default value is none
and
you may specify a template for the dropdown menu
Demo(try the last one):
http://plnkr.co/edit/1yLmarsQFDzcLd0e8Afu?p=preview
How to get it to work?
Based on your plunkr, you should change
<div class="input-group" uib-dropdown auto-close="disabled">
<input type="text" class="form-control" placeholder="Click to start a visual query search..." autocomplete="off" uib-dropdown-toggle/>
<ul class="dropdown-menu" role="menu" ng-if="ctrl.dropdownReady" uib-dropdown-menu template-url="{{ctrl.dropdownTemplateFour}}">
</ul>
<span class="input-group-btn">
<button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i>
</button>
</span>
</div>
to
<div class="input-group" uib-dropdown auto-close="disabled" ng-if="ctrl.dropdownReady">
<input type="text" class="form-control" placeholder="Click to start a visual query search..." autocomplete="off" uib-dropdown-toggle/>
<ul class="dropdown-menu" role="menu" uib-dropdown-menu template-url="{{ctrl.dropdownTemplateFour}}">
</ul>
<span class="input-group-btn">
<button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i>
</button>
</span>
</div>
In which the ng-if="ctrl.dropdownReady" is moved to the div.input-group.
And change
vm.dropdownReady = false;
console.log('vm.dropdownReady =', vm.dropdownReady, ' partial = ', partial);
switch (template) {
case 'word':
partial ? vm.dropdownTemplateFour = 'word-dropdown-dom.template.html' : vm.dropdownTemplateThree = 'word-dropdown-dom.html';
break;
case 'main':
partial ? vm.dropdownTemplateFour = 'main-dropdown-dom.template.html' : vm.dropdownTemplateThree = 'main-dropdown-dom.html';
break;
}
vm.dropdownReady = true;
to
vm.dropdownReady = false;
console.log('vm.dropdownReady =', vm.dropdownReady, ' partial = ', partial);
switch (template) {
case 'word':
partial ? vm.dropdownTemplateFour = 'word-dropdown-dom.template.html' : vm.dropdownTemplateThree = 'word-dropdown-dom.html';
break;
case 'main':
partial ? vm.dropdownTemplateFour = 'main-dropdown-dom.template.html' : vm.dropdownTemplateThree = 'main-dropdown-dom.html';
break;
}
$timeout(function(){
vm.dropdownReady = true;
});
Which has a $timeout wrap the vm.dropdownReady = true;. And you should inject the $timeout by hand;
Keep the menu open
According to the documentation, we can choose the initial state of drop menu with is-open attr. And we can listen the toggle event with on-toggle attr. So if we want to keep the menu open after user clicking the input, we should set the attributes of uib-dropdown like this:
<div class="input-group" uib-dropdown auto-close="disabled" ng-if="ctrl.dropdownReady" is-open="ctrl.open" on-toggle="ctrl.toggled(open)">
And in controller:
vm.toggled = function (open) {
// the parameter `open` is maintained by *angular-ui/bootstrap*
vm.open=open;//we don't need to init the `open` attr, since it's undefined at beginning
}
With these things done, once the menu is open, it doesn't close without user clicking the input again.
Why?
Let's check this snippet:
$templateRequest(self.dropdownMenuTemplateUrl)
.then(function(tplContent) {
templateScope = scope.$new();
$compile(tplContent.trim())(templateScope, function(dropdownElement) {
var newEl = dropdownElement;
self.dropdownMenu.replaceWith(newEl);//important
self.dropdownMenu = newEl;
$document.on('keydown', uibDropdownService.keybindFilter);
});
});
The snippet above shows how does angular-ui/bootstrap use the template-url attr to retrive template and take it into effect. It replaces the original ul element with a newly created element. That's why the uib-dropdown-menu and template-url is missing after clicking the ul. Since they don't exist, you can't change the template-url with angular binding anymore.
The reason executing vm.dropdownReady = true; immediately after the vm.dropdownReady = false; doesn't work is that angular have no chance to dectect this change and execute the "ng-if" to actually remove the dom. You must make the toggling of vm.dropdownReady asynchronous to give angular a chance to achieve this.

Angular UI Boostrap dropdown does not open with [is-open] and [append-to] attributes

I want to use the bootstrap dropdown from the angular ui lib.
I'm getting an issue when I append the dropdown to another DOM element with dropdown-append-to (even dropdown-append-to-body is not working).
I can't have it working only when I use is-open with ng-click to trigger the dropdown.
I reproduced the issue in this plunker.
I'm using bootstrap 3.3.6, angular ui 2.1.3 and angular 1.5.8.
Hope somebody can help me.
You should work with uib-dropdown-toggle and also add an ng-click and toggle test at your controller
Also, dropdown-append-to-body should accept a value (default is false), like so dropdown-append-to-body="true"
<div class="btn-group" uib-dropdown dropdown-append-to-body="true" is-open="test">
<button type="button" ng-click="onClick(test)" class="btn btn-primary" uib-dropdown-toggle>
Dropdown on Body <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="btn-append-to-body">
<!-- list items -->
</ul>
</div>
exmaple plunk

AngularJs + MVC: How to populate page view from a clicked link in shared layout view?

I'm very new to AngularJs & working on a test project using AngularJs + MVC, so apologies if this code looks very botched.
I'm trying to figure out how to make a clicked link in my shared view pass over to my index.cshtml view, in a manner that my index view will know which link was clicked. Ideally, I'd like to do this with angular, so it's seamless. In my MVC project, I created a shared view for my website template. Within this template, is a dropdown, populated via AngularJs after retrieving values from my database. The dropdown contains links on each item within the dropdown. When a user clicks one of the dropdown links, I want to pass the call seamlessly to my index.cshtml view, but I'm not sure on the best way to do this.
Here's the code I have so far, which is retrieving all dropdown values from the database & populating them in my dropdown on my shared view template. This works fine, but now I'm unsure of what to do when a user clicks a link within the dropdown. What I need to happen, is for the table data on my index view to change, based on which link the user clicks in the dropdown.
In my shared view, I'm populating my dropdown values (campaign.Campaign1) via Angular. "SelectedCampaign" is just text of the campaign name that the user's currently selected:
<div style="text-align:center">
<div class="btn-group" ng-controller="menuController">
<button type="button" class="btn btn-danger">{{SelectedCampaign}}</button>
<button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="campaign in campaigns">
{{campaign.Campaign1}}
</li>
</ul>
</div>
</div>
In my Angular MenuController.js script, I'm calling an MVC controller called "GetCampaigns", then returning the result to my shared view's dropdown:
angular.module('dashboardApp', [])
.controller('menuController', function ($scope, $http) {
$http.get('/Account/GetCampaigns')
.success(function (result) {
$scope.SelectedCampaign = result[0]['Campaign1'];
$scope.campaigns = result;
})
.error(function (data) {
console.log(data);
});
})
Thanks for any help you can provide.
Instead of using the "a" link use ng-click and use the ng-click directive to update the current data to the selected campaign data. So put all the campaigns on your scope, then use the ng-click to call a function to select the campaign. Something like:
<li ng-repeat="campaign in campaigns">
<span ng-click="selectCampaign($index)">{{campaign.Campaign1}}</span>
</li>
You can use $index to get the index in the repeat and use that to target the item in the array: https://docs.angularjs.org/api/ng/directive/ngRepeat
e.g.
$scope.selectCampaign = function(index){
$scope.selectedCampaign = $scope.campaigns[index];
}

Angular JS Trying create a clone of header, but directives no longer work on clone

Trying to do something like this
angular.element('.header').addClass('original').clone().insertAfter('.header').addClass('cloned').css('position', 'fixed').css('top', '0').css('margin-top', '0').css('z-index', '500').css('padding-top', '15px').css('padding-bottom', '16px').removeClass('original').hide();
angular.element('.cloned').show();
angular.element('.original').css('visibility', 'hidden');
angular.element('.original').addClass('orginal-header');
And it creates a clone of my header for a sticky header solution, but then I notice the directives I have on a link in the header i.e. a help link which opens/closes a popup menu no longer works.
I.e In my header I have this
<div class="dropdown pull-right" dropdown on-toggle="toggled(open)">
<a class="btn btn-default btn-menu dropdown-toggle" href dropdown-toggle> Help? </a>
<ul class="dropdown-menu">
<li><a ui-sref="help">Get Help?</a></li>
<li><a href open-support-modal>Report an Issue</a></li>
</ul>
</div>
I have a click event on dropdown-toggle which no longer triggers on the copied header.
Any ideas ?
Since you are modifying the DOM, you have to inform angular that it needs to take a second look at things. That's what the $compile service is for. Add this code after your cloning code:
var element = angular.element(".cloned");
$compile(element.contents())(scope);

need inner action list supported angular js dropdown

Can any one provide me reference to angularjs dropdown with action links as shown in the image? From the action list if we mouse over on any lable separate action list should be shown out. Please see the attached screen shot for reference. I should be able to have part2 also as shown in the image. The official Angularjs UI Bootstrap dropdown menu (ui.bootstrap.dropdown) provides only one list of actions (part1 from the image)
As of now I have used the following normal code which is working fine :
<div class="btn-group" dropdown is-open="status.isopen">
<button type="button" class="btn dropdown-toggle" dropdown-toggle>
Save Options <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a href ng-click="saveFilter(ui3DataSet)">Save Filter</a></li>
<li><a href ng-click="saveAsFilter(ui3DataSet)">Save Filter As</a></li>
</ul>
</div>
But now when I mouse over saveFilter(ui3DataSet), another list should be shown on the right side. I am looking for any Angularjs plugin provides this feature readily.
As per #Dmitri Zaitsev, the following solves the issue:
<li class="dropdown-submenu">
<a href ng-click="saveAsFilter(ui3DataSet)">Others</a>
<ul class="dropdown-menu">
<li> One </li>
<li> two </li>
</ul>
</li>
Here is a pure Bootstrap implementation:
http://bootsnipp.com/snippets/featured/multi-level-dropdown-menu-bs3#comments
You could wrap it into Angular directive if you want to (see e.g. the implementation of jQuery Passthrough by UI Utils).
Also other threads that might be of help/related:
Dynamically creating nested navigation menu item using Bootstrap and Angularjs
AngularJS multilevel dropdown menu for a menu structure generated from a recursive directive
Angular UI multi-level Dropdown Toggle

Resources