4

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?

Gargaroz
  • 313
  • 9
  • 28
  • If I understand correctly, you are looking for [**something like this**](http://jsfiddle.net/rcjWu/), but in only one dropdown? – Mistalis Mar 29 '17 at 14:11

2 Answers2

2

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

lealceldeiro
  • 14,342
  • 6
  • 49
  • 80
  • Which is what [I just did (watch it by clicking this link)](http://plnkr.co/edit/LRtlALkTuRFwZMQPWOHe) but unfortunately it doesn't work. – Gargaroz Feb 24 '17 at 15:04
  • Your code is according to what I just said :)OK.Now, I think it only needs a tweak to get it working properly.The problem is that it seems that the component once rendered doesn't notice the template change (it doesn't "watch" it),so you could try(and this is what I meant by saying "repaint") delete the html node and re-insert it (now with the new template).How?Use a `ng-if` condition when click `setDropdownTemplate` set a var to `false`(this will remove the component from dom),then, once done this set that var again to `true` (and this will "repaint" the component again).Pls, Give me feedback – lealceldeiro Feb 24 '17 at 15:25
  • The condition would go something like this: ` – lealceldeiro Feb 24 '17 at 15:28
  • Done, [check it out](http://plnkr.co/edit/LRtlALkTuRFwZMQPWOHe?p=preview), but unfortunately for me it doesn't work yet. – Gargaroz Feb 24 '17 at 16:20
  • 1
    It seems it's just the behavior of the component. That's a pity :( Anyways I'll do some more research. I'll give you some feedback if I find something else useful – lealceldeiro Feb 24 '17 at 17:45
  • @Gargaroz, in the solution described above by me... did your try it enclosing the `readyToShow=true` part inside a `$timeout`? If not, could you try it? – lealceldeiro Mar 29 '17 at 14:45
  • you mean just like [blackmiaool has done in the 4th input (line 37-39) of my plunker fork?](http://plnkr.co/edit/1yLmarsQFDzcLd0e8Afu?p=preview) Because that doesn't work either. – Gargaroz Apr 03 '17 at 08:10
1

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.

blackmiaool
  • 5,234
  • 2
  • 22
  • 39
  • Hi, thank you for your thorough analysis of the problem, however, the 4th input you refer to in this answer doesn't work like expected: the dropdown template changes, but it's necessary to click its input field again to show the new template. What I expect is that the user could actually navigate within that dropdown, passing from template to template, visiting various `
      ` to compose the query he likes.
    – Gargaroz Apr 03 '17 at 07:51
  • @Gargaroz What do you think about it now? :D – blackmiaool Apr 03 '17 at 08:30
  • Awesome, you did it; so if I got it right, `autoclose="disabled"` doesn't prevent the dropdown to "close" once we dynamically change its template, hence the need of manually handle `is-open` with the `on-toggle` function, am I right? – Gargaroz Apr 03 '17 at 08:32
  • What I think? Dude I think you have too few upvotes than the "wrong" answer, sadly. But I can help with 150 shmackeroni just for you dude. There you go, you are a beast. – Gargaroz Apr 03 '17 at 08:34
  • 1
    Thanks a lot :D. The reason why the menu is closed after changing its template is that we destroy the whole dropdown menu with ng-if="ctrl.dropdownReady". It can't have any open state left. – blackmiaool Apr 03 '17 at 08:45