Does ng-grid provide a right click context menu?
I couldn't find it on http://angular-ui.github.io/ng-grid/
However I do remember there was a comprehensive demo page available few weeks back (July 2014) where row level and even cell level right click context menu was shown.
Unfortunately I don't have that URL now and strangely not able to find this on Google anymore.
Thanks in advance.
Yes, ng-context-menu is the way to go. Just don't put the dropdown code withing rowTemplate as the position would be incorrect. Place it outside of ui-grid. The only issue with this approach is to get the current ui-grid row. I decided to save it to controller scope on context menu opening. My rowTemplate:
<script type="text/ng-template" id="member-list.row.html">
<div ng-click="col.isRowHeader || grid.appScope.selectNode(row.entity)" ng-repeat="(colRenderIndex, col) in colContainer.renderedColumns track by col.uid" ui-grid-one-bind-id-grid="rowRenderIndex + '-' + col.uid + '-cell'" class="ui-grid-cell" ng-class="{'ui-grid-row-header-cell': col.isRowHeader, '__selected': row.entity.id===grid.appScope.selectedNodeId }" role="{{col.isRowHeader ? 'rowheader' : 'gridcell' }}" ui-grid-cell style='cursor:pointer' context-menu="grid.appScope.contextMenuEntity = row.entity" data-target="cml_menu"></div>
</script>
Note the context-menu="grid.appScope.contextMenuEntity = row.entity" data-target="cml_menu" part in it. That way I can use contextMenuEntity later wherever needed. Using it in context menu dropdown:
<ul class="menu __context" role="menu" id="cml_menu">
<li class="menu-item" ng-click='blade.showDetailBlade(contextMenuEntity, contextMenuEntity.displayName)'>
<i class="menu-ico fa fa-edit"></i> Manage
</li>
<li class="menu-item" ng-click='delete(contextMenuEntity)'>
<i class="menu-ico fa fa-trash-o"></i> Delete
</li>
</ul>
to get the row
html:
<div id="grid1" ui-grid="vm.gridOptions1" ng-right-button="vm.rightClick($event)" ui-grid-pagination ui-grid-move-columns ui-grid-selection class="grid"></div>
js:
angular.module('Context')
.controller('ContextM', ContextM)
.directive('ngRightButton', ngRightButton );
ContextM.$inject = ['$scope', '$rootScope', '$state', '$timeout'];
ngRightButton.$inject = ['$parse'];
function FoboItemController ($scope, $rootScope, $timeout){
$scope.gridOptions = {data: myData};
$scope.rightClick = function (event) {
var scope = angular.element(event.toElement).scope()
console.log('you clicked on row: ', scope.rowRenderIndex);
};
}]);
app.directive('rightClick', function($parse) {
return function(scope, element, attrs) {
var fn = $parse(attrs.rightClick);
element.bind('contextmenu', function(event) {
scope.$apply(function() {
event.preventDefault();
fn(scope, {$event:event});
});
});
};
});
}
})();
Related
In angularjs to show/hide dynamic submenu, I am adding and removing dynamic classes in js file. Every time when the state changes in url (i.e ui-sref={{mymenu.url}}) sub menu is not visible. If there is no state change sub menu is working properly. Can anyone please suggest.
Html
<li ng-repeat='mymenu in menuItems' ng-click="showHideMenu($event);" >
<a class="iconsize" ui-sref={{mymenu.url}}>
<i class={{mymenu.image}}></i> <strong>{{mymenu.menuName}}</strong>
<span class="fa fa-chevron-down"></span>
</a>
<ul class="submenuHide">
<li class="" ng-repeat='submenu in mymenu.submenu'>
<a>{{submenu.submenuName}}</a>
</li>
</ul>
JS
$scope.showHideMenu = function(event) {
var classname = event.target.parentElement.parentElement.children[1].className;
if(classname == 'submenuHide'){
$(event.target.parentElement.parentElement.children[1]).removeClass('submenuHide');
$(event.target.parentElement.parentElement.children[1]).addClass('submenuShow');
}else if(classname == 'submenuShow'){
$(event.target.parentElement.parentElement.children[1]).removeClass('submenuShow');
$(event.target.parentElement.parentElement.children[1]).addClass('submenuHide');
}
}
A couple things. One, you'll need to make sure your menu is outside of the individual templates you're working with. Two, using ng-class bound to an ng-model ensures that your menu state is included in the digest cycle. This will save you the jqLite and dom parsing logic.
Here's an example plunker.
So your code might look like this:
<body ng-controller="MainCtrl">
<a ui-sref="hello">Hello</a>
<a ui-sref="about">About</a>
<button ng-click="toggleMenu()">Show / Hide Menu</button>
<ui-view></ui-view>
<ul ng-class="{showMenu: show, hideMenu: !show}">
<li ng-repeat="letter in ['a','b','c','d']">{{letter}}</li>
</ul>
</body>
With this JS:
app.controller('MainCtrl', function($scope) {
$scope.show = false;
$scope.toggleMenu = function() {
$scope.show = !$scope.show;
};
});
I can't figure out how to do a multi-state form. The main issue is the form.$errors seem to look at the active state rather then the whole form. In other words, the submit button is meant to be disabled until ALL required questions are answered for the entire form, but it seems to become enabled when all the required questions are answered for the active state.
Here's a simplified Plunker: http://plnkr.co/edit/O49eO4uRlUjoWHlxV6Li?p=preview
And below is my actual code.
View:
<div class="row">
<div class="col-md-4">
<ul class="nav nav-pills nav-stacked">
<li role="presentation" ng-repeat="section in vm.sections[0].sections" ng-if="section.fields">
<a ui-sref="subject.items.new.section({sectionUrl: section.url})">
<span class="badge badge-warning" title="Number of unanswered required questions for {{section.name}}">1</span>
{{section.name}}
</a>
</li>
</ul>
</div>
<div class="col-md-8">
<form class="assessment-item" name="assessmentForm">
<div ui-view></div>
<hr>
<button class="btn btn-primary btn-raised" ng-disabled="assessmentForm.$invalid" ng-click="vm.createCompletedItem()">Save Changes</button>
<button class="btn btn-inverse btn-inverse-danger" back-button>Cancel</button>
</form>
</div>
</div>
Routes:
.state('subject.items.new', {
url: '/items/new/:availableItemUrl',
templateUrl: 'components/items/new.html',
controller: 'ItemNewCtrl',
controllerAs: 'vm',
resolve: {
getAvailableItemsResolve: function(DataService) {
return DataService.availableItems().getList();
},
getUser: function($cmUserData) {
return $cmUserData.getUser(1);
}
}
})
.state('subject.items.new.section', {
url: '/:sectionUrl',
template: '<div class="animated fadeIn"><formly-form model="vm.completedItem.answers" fields="vm.fields"></formly-form></div>',
controller: function($scope, $stateParams, lodash) {
var vm = this;
var _ = lodash;
vm.questions = _.filter($scope.vm.sections[0].sections, { 'url' : $stateParams.sectionUrl });
vm.fields = vm.questions[0].fields;
vm.completedItem = $scope.vm.completedItem;
},
controllerAs: 'vm'
})
itemNewCtrl:
(function() {
'use strict';
angular
.module('casemanagerApp')
.controller('ItemNewCtrl', ItemNewCtrl);
function ItemNewCtrl($stateParams, $filter, DataService, lodash, getUser, getAvailableItemsResolve) {
var vm = this;
var _ = lodash;
vm.item = DataService.completedItems().one();
vm.availableItems = getAvailableItemsResolve;
vm.sections = _.filter(vm.availableItems, { 'url' : $stateParams.availableItemUrl });
vm.completedItem = DataService.completedItems().one();
vm.completedItem.subjectId = $stateParams.subjectId;
vm.completedItem.name = vm.sections[0].name;
vm.completedItem.probationOfficer = getUser.firstName + ' ' + getUser.lastName;
vm.completedItem.label = 'Final';
}
})();
The issue is due to the way you have things set up. When I switch tabs, angular-ui-router removes the view entirely and therefore angular only sees one formly-form, therefore only one formly-form is validated at a time. I would recommend against using angular-ui-router and instead just use a directive (like angular-ui-bootstrap's tabset directive) which uses ng-show to keep both forms on the page at a time and therefore both will be taken into account when doing validation. Here's an example that might help you there.
All you need to do is set a flag, like "formComplete" in the model and then flip it on once they get to the last question...we have similar issues at work with something like this and that's how we work around it.
Alternatively you could simply move the submit button to the last page
how to write a service or factory for bootstrap tabs using angularJs.
I placed in one controller but need a common function(repeated code) for different controllers.
<ul class="nav nav-tabs">
<li data-ng-class="{active:tab===0}">
<a href ng-click="changeTab(0)"> <i class="fa fa-list"></i></a>
</li>
<li data-ng-class="{active:tab===1}">
<a href ng-click="changeTab(1)"><i class="fa fa-user"></i></a>
</li>
</ul>
<div data-ng-class="{activeTab:tab===0}" ng-show="isActiveTab(0)">
tab1
</div>
<div data-ng-class="{activeTab:tab===1}" ng-show="isActiveTab(1)">
tab2
</div>
controller
$scope.tab = 0;
$scope.changeTab = function(newTab){
$scope.tab = newTab;
};
$scope.isActiveTab = function(tab){
return $scope.tab === tab;
};
Managing tabs is a view concern. Rather than implementing a factory, I recommend creating two directives: tabContainer and tab. The tab directive registers itself with the parent tabContainer using the require attribute to access the parent directive's controller API.
Demo
Usage
<tab-container selected="tab2">
<tab name="tab1">tab1</tab>
<tab name="tab2">tab2</tab>
</tab-container>
Parent Directive
The parent directive publishes the following controller API that the child tab directives will access:
tabContainer controller
// registers this tab with the parent tabContainer
this.register = function(element) {
$scope.tabs.push(element);
}
// returns the selected tab object whose
// name property indicates which tab is active
this.getSelected = function() {
return $scope.selectedTab;
}
Child Directive
The tab directive is able to access the parent controller by requiring the parent directive in its directive definition, and accessing the parent directive's controller as the 4th argument to the tab directive's link function:
tab directive definition
scope: true,
require: '^tabContainer',
link: function(scope, element, attr, tabContainer) {
// set the tab so that it is visible in the tab directive's scope.
scope.tab = { name: attr.name, element:element};
scope.selectedTab = tabContainer.getSelected();
tabContainer.register(scope.tab);
}
The scope is set to true so that each tab will create its own child scope and not interfere with the scope of other tabs.
Template Files
For example purposes, the directive templates are embedded in the HTML:
<script type="text/ng-template" id="tabContainer.html">
<ul class="nav nav-tabs">
<li ng-repeat="tab in tabs" data-ng-class="{active:selectedTab.name===tab.name}">
<a href ng-click="changeTab(tab)"> <i class="fa fa-list">{{tab.name}}</i></a>
</li>
</ul>
<ng-transclude></ng-transclude>
</script>
<script type="text/ng-template" id="tab.html">
<div data-ng-class="{activeTab:selectedTab.name===tab.name}" ng-show="selectedTab.name === tab.name">
<ng-transclude></ng-transclude>
</div>
</script>
It is recommended to move these to dedicated HTML files.
Changing the Active Tab
The user is able to change the active tab by clicking the tab link. This is achieved by publishing a $scope function in the parent controller:
$scope.changeTab = function(tab) {
$scope.selectedTab.name = tab.name;
}
Creating a Tabs Module
The beauty of AngularJS and its pluggable modular architecture is that you can extend the AngularJS directive ecosystem, and have the directives work together seamlessly. For example, you could encapsulate the above tabs directive into a tabs module, and even use the ngRepeat directive to bind the tabs.
Demo
Controller
var app = angular.module('app',['tabs']);
app.controller('ctrl', function($scope) {
$scope.tabData = [
{ name: 'tab1', body: 'You selected tab1!'},
{ name: 'tab2', body: 'You selected tab2!'},
{ name: 'tab3', body: 'You selected tab3!'},
{ name: 'tab4', body: 'You selected tab4!'},
];
});
View
<div class="container" ng-controller="ctrl">
<tab-container selected="tab1">
<tab ng-repeat="tab in tabData" name="{{tab.name}}">{{ tab.body }} </tab>
</tab-container>
</div>
Hi I write it with out using any service or factory
see the example
<ul ng-init="tab = 1">
<li ng-class="{active:tab===1}">
<a href ng-click="tab = 1">Tab1</a>
</li>
<li ng-class="{active:tab===2}">
<a href ng-click="tab = 2">Tab2</a>
</li>
<li ng-class="{active:tab===3}">
<a href ng-click="tab = 3">Tab3</a>
</li>
<br><br>
<p ng-show="tab === 1"> Tab1 content </p>
<p ng-show="tab === 2"> Tab2 content</p>
<p ng-show="tab === 3"> Tab3 content</p>
</ul>
Dynamically change it through controller see the working example here
The structure for controllers/services in Angular are explained well here:
https://github.com/johnpapa/angular-styleguide
In app.js, we declare an angular application, give it a name, and any dependencies (ng-route / ng-grid). $http calls should be made using a factory or a service, and the controller should call the service to fetch or post data. From the angular documentation, "services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app."
https://docs.angularjs.org/guide/services
app.js:
var app = angular.module('appname', []);
app.factory('httpResponseErrorInterceptor'...
app.config(['$httpProvider',...
Controller:
angular.module('appname').controller("NameCtrl", ["$scope", "$log", "$window", "$http", "$timeout", "SomeService",
function ($scope, $log, $window, $http, $timeout, TabService) {
//your controller code
$scope.tab = 0;
$scope.changeTab = function(newTab){
$scope.tab = newTab;
};
$scope.isActiveTab = function(tab){
return $scope.tab === tab;
};
}
]);
Service:
angular.module('appname').service("TabService", function () {
//Your code for shared functionality regarding tab service
var currentTab = {};
return {
currentTab : currentTab
}
});
I have a number of links on the page, dynamically generated like so:
<a href="#" class="more-info-item" ng-click="showStats(item, $event)">
More information
</a>
I also have a simple custom template that should show an item's name:
<script type="text/ng-template" id="iteminfo.html">
<div class="item-name">
{{item.name}}
</div>
</script>
What I would like to do is: when the link is clicked, to dynamically compile the template, and insert it in the DOM right after the link. I tried using $compile within showStats method to compile the template, but I got an error that $compile wasn't found. How would I go about doing this (and also provide item as part of the scope for the newly generated template)?
Here is a solution using a custom directive which injects the item dynamically using ng-if:
View Solution with Plunker
html:
<script type="text/ng-template" id="iteminfo.html">
<div class="item-name" ng-if="item.visible">
{{item.name}}
</div>
</script>
<div ng-repeat="item in items" >
<a href="#" class="more-info-item" more-info="item" ng-click="item.visible =!item.visible">
More information
</a>
</div>
script:
angular.module('app', [])
.controller('ctrl', function($scope) {
$scope.items = [{name:'apples', visible:false},{name:'pears', visible:false},{name:'oranges', visible:false}];
})
.directive('moreInfo', function($compile,$templateCache) {
return {
restrict: 'A',
scope: '=',
link: function(scope, element, attr) {
var itemInfo = angular.element($templateCache.get('iteminfo.html'));
var lfn = $compile(itemInfo);
element.parent().append(itemInfo);
lfn(scope);
}
};
});
You can use the built-in ngInclude directive in AngularJS
Try this out
Working Demo
html
<div ng-controller="Ctrl">
<a href="#" class="more-info-item" ng-click="showStats(item, $event)">
More information
</a>
<ng-include src="template"></ng-include>
<!-- iteminfo.html -->
<script type="text/ng-template" id="iteminfo.html">
<div class="item-name">
{{item.name}}
</div>
</script>
</div>
script
function Ctrl($scope) {
$scope.flag = false;
$scope.item = {
name: 'Manu'
};
$scope.showStats = function (item, event) {
$scope.item = item;
$scope.template = "iteminfo.html";
}
}
Using the example mentioned here, how can I invoke the modal window using JavaScript instead of clicking a button?
I am new to AngularJS and tried searching the documentation here and here without luck.
Thanks
OK, so first of all the http://angular-ui.github.io/bootstrap/ has a <modal> directive and the $dialog service and both of those can be used to open modal windows.
The difference is that with the <modal> directive content of a modal is embedded in a hosting template (one that triggers modal window opening). The $dialog service is far more flexible and allow you to load modal's content from a separate file as well as trigger modal windows from any place in AngularJS code (this being a controller, a service or another directive).
Not sure what you mean exactly by "using JavaScript code" but assuming that you mean any place in AngularJS code the $dialog service is probably a way to go.
It is very easy to use and in its simplest form you could just write:
$dialog.dialog({}).open('modalContent.html');
To illustrate that it can be really triggered by any JavaScript code here is a version that triggers modal with a timer, 3 seconds after a controller was instantiated:
function DialogDemoCtrl($scope, $timeout, $dialog){
$timeout(function(){
$dialog.dialog({}).open('modalContent.html');
}, 3000);
}
This can be seen in action in this plunk: http://plnkr.co/edit/u9HHaRlHnko492WDtmRU?p=preview
Finally, here is the full reference documentation to the $dialog service described here:
https://github.com/angular-ui/bootstrap/blob/master/src/dialog/README.md
To make angular ui $modal work with bootstrap 3 you need to overwrite the styles
.modal {
display: block;
}
.modal-body:before,
.modal-body:after {
display: table;
content: " ";
}
.modal-header:before,
.modal-header:after {
display: table;
content: " ";
}
(The last ones are necessary if you use custom directives) and encapsulate the html with
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
Open modal windows with passing data to dialog
In case if someone interests to pass data to dialog:
app.controller('ModalCtrl', function($scope, $modal) {
$scope.name = 'theNameHasBeenPassed';
$scope.showModal = function() {
$scope.opts = {
backdrop: true,
backdropClick: true,
dialogFade: false,
keyboard: true,
templateUrl : 'modalContent.html',
controller : ModalInstanceCtrl,
resolve: {} // empty storage
};
$scope.opts.resolve.item = function() {
return angular.copy(
{name: $scope.name}
); // pass name to resolve storage
}
var modalInstance = $modal.open($scope.opts);
modalInstance.result.then(function(){
//on ok button press
},function(){
//on cancel button press
console.log("Modal Closed");
});
};
})
var ModalInstanceCtrl = function($scope, $modalInstance, $modal, item) {
$scope.item = item;
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
Demo Plunker
The AngularJS Bootstrap website hasn't been updated with the latest documentation. About 3 months ago pkozlowski-opensource authored a change to separate out $modal from $dialog commit is below:
https://github.com/angular-ui/bootstrap/commit/d7a48523e437b0a94615350a59be1588dbdd86bd
In that commit he added new documentation for $modal, which can be found below:
https://github.com/angular-ui/bootstrap/blob/d7a48523e437b0a94615350a59be1588dbdd86bd/src/modal/docs/readme.md.
Hope this helps!
Quick and Dirty Way!
It's not a good way, but for me it seems the most simplest.
Add an anchor tag which contains the modal data-target and data-toggle, have an id associated with it. (Can be added mostly anywhere in the html view)
Now,
Inside the angular controller, from where you want to trigger the modal just use
angular.element('#myModalShower').trigger('click');
This will mimic a click to the button based on the angular code and the modal will appear.
Different version similar to the one offered by Maxim Shoustin
I liked the answer but the part that bothered me was the use of <script id="..."> as a container for the modal's template.
I wanted to place the modal's template in a hidden <div> and bind the inner html with a scope variable called modal_html_template
mainly because i think it more correct (and more comfortable to process in WebStorm/PyCharm) to place the template's html inside a <div> instead of <script id="...">
this variable will be used when calling $modal({... 'template': $scope.modal_html_template, ...})
in order to bind the inner html, i created inner-html-bind which is a simple directive
check out the example plunker
<div ng-controller="ModalDemoCtrl">
<div inner-html-bind inner-html="modal_html_template" class="hidden">
<div class="modal-header">
<h3>I'm a modal!</h3>
</div>
<div class="modal-body">
<ul>
<li ng-repeat="item in items">
<a ng-click="selected.item = item">{{ item }}</a>
</li>
</ul>
Selected: <b>{{ selected.item }}</b>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</div>
<button class="btn" ng-click="open()">Open me!</button>
<div ng-show="selected">Selection from a modal: {{ selected }}</div>
</div>
inner-html-bind directive:
app.directive('innerHtmlBind', function() {
return {
restrict: 'A',
scope: {
inner_html: '=innerHtml'
},
link: function(scope, element, attrs) {
scope.inner_html = element.html();
}
}
});