Angularjs Dropdown closes when $location.search parameters are changed - angularjs

I'm building an ecommerce site. I have a directive called "horizontalShoppingByCategoriesLayout" that watches the current filter criteria (to filter products being returned) and as the user changes those filter criteria the directive sets the search parameters $location.search(..).
...
filterParameters['searchtext'] = $scope.searchText;
filterParameters['order'] = searchorder;
filterParameters['page'] = page;
filterParameters['limit'] = limit;
$location.search(filterParameters);
On that same directive my code is set up to watch the url, and if it changes (due to changes in filter criteria) then to do a search to return products.
$scope.$watch(function () { return $location.url(); }, function (url){
if (url){
$timeout(function() {
getProducts();
});
}
});
The filter criteria are defined in a directive called "filterCriteriaVarietyCategoriesHorizontal" that is defined in the template of "horizontalShoppingByCategoriesLayout".
<div class="pull-leftt" ng-if='numberProducts != undefined && numberProducts > 0'>
<filter-criteria-horizontal execute-criteria-search=$parent.executeCriteriaSearch category-id=$parent.categoryId search-criteria-list=$parent.searchCriteriaList></filter-criteria-horizontal>
</div>
When a user hovers over the filter criteria category button, this triggers an angular event that causes an Angular-UI Bootstrap dropdown to drop down and show the options for that filter criteria category. The user can then click on the filter criteria option, which "horizontalShoppingByCategoriesLayout" detects and then calls a change on the $location search parameters (which changes the url and a webservice call for products is initiated).
The filter criteria options in the dropdown are a set of checkboxes. My problem is when the user clicks on one of these checkboxes, it causes the dropdown to close, which should only happen when the user hovers off of the button that initiated the dropdown opening. I already fixed the problem where the clicking on the checkbox it's self causes the dropdown to close by calling ng-click="$event.stopPropagation()" on the checkbox. Here is my code for the filtercriteria category, dropdown, and checkboxes.
<div class="coll-md-12" ng-mouseleave="closeAll()">
<div class="btn-group" dropdown is-open="status.isopen">
<button ng-mouseleave="close()" ng-mouseenter="open()" type="button" ng-class="{'filter-criteria-variety-category-name-hover': filterCriteriaCategoryActive}" class="btn btn-link dropdown-toggle filter-criteria-variety-category-name" dropdown-toggle ng-disabled="disabled">
{{searchCriteria.criteriaName}}<span class="caret"></span>
</button>
<div class="dropdown-menu">
<div class="search-criteria-values" ng-mouseleave="close()" ng-mouseenter="keepOpen()">
<div ng-if="searchCriteria.imageBasedFilter == true">
<filter-criteria-options-visual filter-criteria-options=$parent.searchCriteria.criteriaOptionValues></filter-criteria-options-visual>
</div>
<div ng-if="searchCriteria.imageBasedFilter == false">
<div class="col-md-12 no-padding" ng-repeat="searchCriteriaOption in searchCriteria.criteriaOptionValues">
<div class="pull-left filter-criteria-checkbox-item">{{searchCriteriaOption.criteriaOption}} <input ng-click="$event.stopPropagation()" id="{{searchCriteriaOption.criteriaOption}}" type="checkbox" ng-model="searchCriteriaOption.checked"/></div>
</div>
</div>
</div>
</div>
</div>
I'm sure that it is adding search parameters to the url with $location.search(...) that is causing the dropdown to close because if I comment out the line that adds the (filter criteria to the) search parameters to the url in the directive "horizontalShoppingByCategoriesLayout", then the dropdown does not close.
...
filterParameters['searchtext'] = $scope.searchText;
filterParameters['order'] = searchorder;
filterParameters['page'] = page;
filterParameters['limit'] = limit;
//$location.search(filterParameters);
With the checkboxes in the dropdown I was able to stop the dropdown from closing by calling stopPropagation() on the click event, but for a change in the $location search parameters I don't know how to stopPropogation for that change.
Does anyone have an idea how to cause the dropdown to not close when changes are made to the $location.search parameters?

I had the same issue then I looked into the source code of angular boostrap ui and found:
$scope.$on('$locationChangeSuccess', function() {
scope.isOpen = false;
});
And a hacky solution would be remove the listener
$scope.$$listeners.$locationChangeSuccess = [];
in your dropdown controller

The solution from user3217868 worked well for me but I had to execute the code once the document was fully loaded like on my code below.
angular.element(document).ready(function () {
$scope.$$listeners.$locationChangeSuccess = [];
});
Posting here in case it may be helpful to someone else.

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.

delete the selected item from list

Im using ionic framework and Im trying to create a phone list which can add and delete the user entered phone no.Here the user entered numbers are listed with check box on clicking the add button.When the user selects the check-box and clicks the delete button, he must be able to delete the selected check box phone number.Here the problem is while using the delete button, it doesn't delete the selected check box instead it is deleting the first value entered in the list. So please help me to delete only user selected check-box items.
html code:
<div>
<ion-checkbox ng-model="phoneno" ng-repeat="y in phonelist">
<span data-ng-bind="y"> {{y}}</span> </ion-checkbox>
<button ng-click="remove($index)" value="Delete">Delete</button><br>
</div>
<br>
<br>
<div>
<!label class="item item-input item-floating-label">
<input ng-maxlength="10" ng-model="phone"> <br>
<button ng-click="add()" value="Add">Add</button><br>
<!/label>
</div>
</ion-content >
</ion-view>
js code:
.controller('PlaylistCtrl', function($scope, $stateParams) {
})
.controller('addAdmin',['$scope',function($scope){
$scope.phonelist=[];
$scope.add=function(phone){
$scope.phonelist.push($scope.phone);
$scope.phone='';
}
$scope.remove=function(uuid){
var x=$scope.phonelist[uuid];
$scope.phonelist.splice(uuid,1);
}
}]);
Sorry for my english. I'm foreigner..
The problem is that your loop ends before the button, so when the button gets clicked the $index is always 0.
Because its not inside the element "ion-checkbox".
Here is my solution: put ng-click inside the checkbox and call to function with the $index.
And in the js, save the index on a scope var. So if the delete button gets clicked, delete the index that you saved on the previous function.
I hope that i helped.
It seems like you mean something else than your code says. You probably want to delete all phones that are checked, when clicking the button. Therefore you don't need the $index property, but just loop through the phones and delete the ones that are checked.
You will have to keep track of a 'checked' property of each phone, so you know which are checked. You can do this by using an object which holds the phone information, instead of just a string:
<div>
<!-- ng-model to a property of the phone that keeps track if the phone is checked -->
<ion-checkbox ng-model="y.checked"
ng-repeat="y in phonelist">
<span data-ng-bind="y.number">{{ y.number }}</span>
</ion-checkbox>
<button ng-click="removeSelected()" value="Delete">Delete</button><br>
</div>
<!-- ng-model to a property of the phone object -->
<input type="text" ng-model="phone.number" />
And in your controller:
$scope.add = function() {
$scope.phonelist.push($scope.phone);
}
$scope.removeSelected = function() {
var i = $scope.phonelist.length;
// reversed loop because you change the array
while (i--) {
var phone = $scope.phonelist[i];
// If phone is checked, remove from list
if(phone.checked) {
$scope.phonelist.splice(i, 1);
}
}
}
See this jsfiddle
Or see this jsfiddle where I included Ionic
It seems that you're mixing the add and remove functions, try to separate those as below.
.controller('addAdmin',['$scope',function($scope){
$scope.phonelist=[];
//Add function
$scope.add=function(phone){
$scope.phonelist.push(phone);
$scope.phone='';
}
//Remove function
$scope.remove = function(index){
$scope.phonelist.splice(index, 1);
};
}]);
add the following code in your controller
$scope.remove = function(index){
$scope.phonelist.splice(index, 1);
}
and that should work

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];
}

AngularJS with ui-router ng-hide not working after initially working

I've been struggling with a ng-hide issue in combination with using ui-router. Simple app. Index.html shows some data via the "notes" route, you click on "detail" and you go to the sub route "notes.note" to view the detail just below the other records. The "detail" html has a "Save" & "Cancel" button.
Now there is an "Add New" button when you are not viewing the detail with the attribute ng-hide="HideAddNew". "HideAddNew" is a $scope variable in the controller. When I click "detail" on a row I have this ng-click="toggleAddNew()" on the link which in turn calls this
$scope.toggleAddNew= function()
{
$scope.HideAddNew=($scope.HideAddNew ? false : true);
}
That works perfectly, my detail shows and my "Add New" button has disappeared. Now on the detail when I click "Cancel" it fire off the ng-click="hideData()" which calls the function:
$scope.hideData=function()
{
$scope.toggleAddNew();
$state.go('notes');
}
And now my "Add New" has disappeared even though the variable is set to false, i.e. Don't hide. I've tried $timeout in that "hideData" function and in the "toggleAddNew" function. I've tried putting "$scope.toggleAddNew();" after the "$state.go('notes');" too. I don't want to resort to manually adding and removing classes. AngularJS ver: v1.3.15 , ui-router ver: v0.2.13 Thanx all :)
EDIT
Would the below work Tony?
<button ng-if="HideAddNew" ng-click="SelectRoute('notenew')" class="btn btn-primary">
<span class="glyphicon glyphicon-plus -glyphicon-align-left"></span>Add New</button>
Perhaps you could simplify and use ng-switch instead.
Something like this:
<ul ng-switch="expression">
<li ng-switch-when="firstThing">my first thing</li>
<li ng-switch-when="secondThing">my second thing</li>
<li ng-switch-default>default</li>
</ul>
Alternatively, maybe you could use ng-if or ng-show instead of ng-hide, eg:
<p ng-if="HideAddNew">it's here!</p>
<p ng-if="!HideAddNew">it's not here.</p>
Edit
If I understand what you're trying to achieve exactly, I would use ng-show with an ng-click:
Controller:
$scope.addNew = false;
View:
<button ng-show="!addNew" ng-click="addNew = true">Add New</button>
<button ng-show="addNew" ng-click="save()">Save</button>
<button ng-show="addNew" ng-click="addNew = false">Cancel</button>
Example

Pass object to template and render it on current view

There is a list of users loaded from an api
<div ng:controller="UserController">
<li ng-repeat="user in users">
<a ng:click="select(user)">
{{user.first_name}} {{user.last_name}}
</a>
</li>
</div>
When a user is clicked I want to open an extra view that shows some detailed information of the user. Imagine the view like this
The small blue area is the selected user and the big blue area the container that shows detailed user information.
function UserController($scope){
$scope.select = function(user){
console.log(user);
}
}
So when the user is clicked I can log the user object. It works until this point. But I absolutely have no idea how to open the extra container filled with user data.
Is there a way to simply load a template from file system, pass the object and append the whole thing to the current view? How would I do this with angular.js?
No need to load templates or anything. Just put the data in the scope, and use expressions in the big blue section that reference the data. Angular will handle changing the displayed content when the selection is changed. You can hide the relevant section with ng-show until the data get selected.
Here's a fiddle example.
function UserController($scope){
$scope.users = [{name:'me',email:'mine'}, {name:'you',email:'yours'}];
$scope.selectedUser = null;
$scope.select = function(user){
$scope.selectedUser = user;
}
}
and
<div ng-controller="UserController">
<button class="btn" ng-click="select(users[0])">User 1</button>
<button class="btn" ng-click="select(users[1])">User 2</button>
<hr>
<div ng-show="selectedUser != null">
<div>{{selectedUser.name}}</div>
<div>{{selectedUser.email}}</div>
</div>
You could highlight the corresponding small blue box with a similar ng-class or ng-style attribute (e.g. ng-class="{active: selectedUser == user}"). See What is the best way to conditionally apply a class?

Resources