I have an angular modal that works with a separate button, but need to have it popup when a row in the slickgrid is double clicked. How should I structure this code:
grid.onDblClick.subscribe(function (e, args) {
//make the angular modal popup
});
With an angular modal, I would normally just select the dom element, then add an ng-click="openModal()" into the div tag but I'm having trouble even locating the div in chrome dev tools. Any ideas?
Currently, I have the slickgrid wrapped in a directive, meaning that my html looks like this:
<div class="gridContainer" ng-controller="EditModalCtrl">
<div class="modalTest" style='width:100%;'>
<div style='width:100%;height:500px;' all-batches-grid></div>
</div>
</div>
In my slickgrid directive, I have:
angular.module('epDefaultApp.allBatchesDirective', [])
.directive('allBatchesGrid', [function () {
return {
restrict: 'EA',
link : function(scope, element, attrs){
grid.onDblClick.subscribe(function (e, args) {
$('.addButton').removeClass('addText');
$('#addEditButton').addClass('editButton');
$('.addButton').addClass('editText');
var item = grid.getDataItem(args.row);
scope.openPrefill('lg', item);
});
}
}
}]);
Here is my controller as well:
angular.module('epDefaultApp.editModalController', [])
.controller('EditModalCtrl', ['$scope', '$modal', '$log', 'toastr', function ($scope, $modal, $log, toastr) {
$scope.openPrefill = function (size, selectedRowObject) {
$scope.selectedRow = {
vendorName: selectedRowObject[0],
purchaseNumber: selectedRowObject[3],
invoiceAmount: selectedRowObject[6],
invoiceDate: selectedRowObject[10],
invoiceNumber: selectedRowObject[1],
dueDate: selectedRowObject[9],
checkCode: '0',
memo: selectedRowObject[5]
};
}]);
The reason that this works is that the scope variable within the directive is actually linked to $scope in the EditModalCtrl. This happens when we first initialize the grid directive in the HTML file.
Related
Please help fix the script.
i make simply directive:
angular.module('menus', [])
.directive("epMenu", ['$timeout', '$state',
function ($timeout, $state) {
return {
link: function (scope, element, attrs) {
scope.goTo = function(link) {
console.log('go to', link);
};
var navigationElem = angular.element("<div class='ep-menu-navigation'><li ng-click='scope.goTo('main')'>qwerty</li></div>");
angular.element('body').append(navigationElem);
},
restrict: "EACM"
}
}]);
but it does not work. I need to when you press the button, start function goTo()
Now the console following error message:
VM436 angular.js:12520 Error: [$injector:unpr] Unknown provider: $stateProvider <- $state <- epMenuDirective
live example
$state is a provider that is registered in the ui.router module, so you have to lay that dependency:
angular.module('menus', ["ui.router"])
Also, if you are building the template dynamically in the link function, you have to compile it so that angular can apply its actions to it:
.directive("epMenu", ['$timeout', '$state', '$compile',
function ($timeout, $state, $compile) {
return {
link: function (scope, element, attrs) {
scope.goTo = function(link) {
console.log('go to', link);
};
var navigationElem = angular.element("<div class='ep-menu-navigation'><li ng-click='goTo('main')'>qwerty</li></div>");
$compile(navigationElem)(scope, function(cloned){
angular.element('body').append(cloned);
});
},
restrict: "EACM"
}
}]);
You also had some other errors in your code:
Missing the angular ui router script
Using scope.goTo instead of just goTo
Not escaping the quotes in the goTo function
Using jqLite, you cannot use angular.element('body'), instead use the $document service
Here is a working example.
First of all you should have ui.router injected as dependency to your module.
angular.module('menus',["ui.router"])
If you want to change the state when clicked on a link created by a directive, create a controller for the directive.
use the $state.go in the controller to redirect.
In my answer, I used a controller, which changes the state to menu when clicked on the link of directive.
Here is the code to achieve it,
(function() {
angular.module('menus')
directive('epMenu', function () {
var controller = ['$scope','$state', function ($scope,$state) {
$scope.goTo = function(link) {
$state.go(link)
};
}],
template = "<div class='ep-menu-navigation'><li ng-click='goTo('main')'>qwerty</li></div>";
return {
restrict: 'EA', //Default in 1.3+
controller: controller,
template: template
};
});
}());
The error inside your code is because it does not find dependency for $state. you have to add angular.module('menus', ["ui.router"]) because $state is registered inside ui-router.also you have to add proper js for ui-router. You can bind click event inside link function.
angular.module('menus', ["ui.router"]).directive("epMenu", ['$timeout', '$state',
function($timeout, $state) {
return {
link: function(scope, element, attrs) {
element.find('button').bind('click', function() {
console.log("click");
});
},
restrict: "EACM"
}
}
]);
angular.module('menus',
["ui.router"])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.min.js"></script>
<div ng-app="menus">
<ep-menu>
this is normal text
<button>
Click Me
</button>
</ep-menu>
</div>
I've got an Angular view thusly:
<div ng-include="'components/navbar/navbar.html'" class="ui centered grid" id="navbar" onload="setDropdown()"></div>
<div class="sixteen wide centered column full-height ui grid" style="margin-top:160px">
<!-- other stuff -->
<import-elements></import-elements>
</div>
This is controlled by UI-Router, which is assigning the controller, just FYI.
The controller for this view looks like this:
angular.module('pcfApp')
.controller('ImportElementsCtrl', function($scope, $http, $location, $stateParams, $timeout, Framework, OfficialFramework) {
$scope.loadOfficialFrameworks();
// other stuff here
});
The <import-elements> directive, looks like this:
angular.module('pcfApp').directive('importElements', function($state, $stateParams, $timeout, $window, Framework, OfficialFramework) {
var link = function(scope, el, attrs) {
scope.loadOfficialFrameworks = function() {
OfficialFramework.query(function(data) {
scope.officialFrameworks = data;
$(".ui.dropdown").dropdown({
onChange: function(value, text, $item) {
loadSections($item.attr("data-id"));
}
});
window.setTimeout(function() {
$(".ui.dropdown").dropdown('set selected', data[0]._id);
}, 0);
});
}
return {
link: link,
replace: true,
templateUrl: "app/importElements/components/import_elements_component.html"
}
});
I was under the impression that I'd be able to call the directive's loadOfficialFrameworks() method from my controller in this way (since I'm not specifying isolate scope), but I'm getting a method undefined error on the controller. What am I missing here?
The problem is that your controller function runs before your link function runs, so loadOfficialFrameworks is not available yet when you try to call it.
Try this:
angular.module('pcfApp')
.controller('ImportElementsCtrl', function($scope, $http, $location, $stateParams, $timeout, Framework, OfficialFramework) {
//this will fail because loadOfficialFrameworks doesn't exist yet.
//$scope.loadOfficialFrameworks();
//wait until the directive's link function adds loadOfficialFrameworks to $scope
var disconnectWatch = $scope.$watch('loadOfficialFrameworks', function (loadOfficialFrameworks) {
if (loadOfficialFrameworks !== undefined) {
disconnectWatch();
//execute the function now that we know it has finally been added to scope
$scope.loadOfficialFrameworks();
}
});
});
Here's a fiddle with this example in action: http://jsfiddle.net/81bcofgy/
The directive scope and controller scope are two differents object
you should use in CTRL
$scope.$broadcast('loadOfficialFrameworks_event');
//And in the directive
scope.$on('loadOfficialFrameworks_event', function(){
scope.loadOfficialFrameworks();
})
I have the following directive for a popup with multiple templates:
app.directive('popup', function ($http, $rootScope, $templateCache, $compile, $parse, Popup) {
return {
link: function ($scope, $element, $attrs) {
$element.bind('click', function () {
var data = {
index: $attrs.index
}
$http.get('/partial/' + $attrs.popup + '.html', {cache: $templateCache}).success(function (tplContent) {
var mainElement = angular.element(document.getElementById('main'));
mainElement.append($compile(tplContent)($scope));
Popup.show(data);
});
});
}
}
});
I've pinned a visibility flag to $rootScope (to make the popup visible by css) along with index and item that came in data object. and the popup template looks like this:
<section class="popup" ng-class="{'show': popup.visibility}">
<h1>{{data[popup.index].title}}<h1>
<p>{{data[popup.index].message}}<p>
</section>
The directive loads the popup template and appends it to my mainElement, but doesn't apply scope to. so popup doesn't appear and no data is bound to it.
you need to pass $scope.$parent instead of $scope for compiling. because you are in the child $scope.
I'm trying to write a custom directive but the attribute the directive is observing only seems to change on the initial scope change. After that, the binding in the view (observed with Firebug) doesn't update any longer. This seems like a scope problem but I'm out of ideas.
JSFiddle Link showing code with problem: http://jsfiddle.net/catalyst156/2gp78/ (contents of fiddle below, but it might be useful to mess around with the fiddle itself).
Controller:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js></script>
<script>
var myApp = angular.module('myApp', []);
myApp.directive('testDirective', function () {
return {
restrict: 'A',
replace: true,
scope: {
toggle: '#'
},
link: function (scope, element, attrs) {
console.log('...linking testDirective...');
attrs.$observe('toggle', function (value) {
console.log('directive: toggle observer');
if ('true' === value) {
console.log('directive: toggle=true');
} else {
console.log('directive: toggle=false');
}
});
}
};
});
myApp.controller('myCtrl', ['$scope', '$log', function ($scope, $log) {
$scope.toggleState = false;
$scope.testToggle = function () {
$scope.toggleState = true;
$log.log('controller: toggleState=' + $scope.toggleState);
setTimeout(turnToggleOff, 2000);
};
function turnToggleOff() {
$scope.toggleState = false;
$log.log('controller: toggleState=' + $scope.toggleState);
}
}]);
HTML:
<div ng-app="myApp">
<div ng-controller="myCtrl">
<div test-directive toggle={{toggleState}}></div>
<button ng-click="testToggle()">Toggle Me</button>
</div>
</div>
Console Output:
1: ...linking testDirective...
2: directive: toggle observer
3: directive: toggle=false
4: controller: toggleState=true
5: directive: toggle observer
6: directive: toggle=true
7: controller: toggleState=false
I can see the link working and the initial state is set to false as expected (lines 1-3). The button is clicked and the observer is notified of the change (lines 4-6). However, when the timer expires and 'toggleState=false', the observer never picks up that change. (multiple button presses beyond only show the console output from the controller and the observer is never fired again).
The issue is that angular is unaware of changes made by a setTimeout(). Said another way- setTimeout() does not trigger a $digest cycle. Angular provides instead $timeout that does the same thing as setTimeout() but it triggers $digest so Angular sees the changes (which in turn will cause your observe to fire).
So just pass in the $timeout dependency:
myApp.controller('myCtrl', ['$scope', '$log', '$timeout', function ($scope, $log, $timeout) {...}
And switch your timeout call:
$timeout(turnToggleOff, 2000);
Here's an updated fiddle
It's possible to intercept current event object in ng-click like handlers by using $event property.
But is it possible to get the element from which the method has been called?
like for example:
<div ng-controller='myController'>
<div>{{distortThatDiv($element)}}</div>
</div>
If you're going to manipulate the DOM, you really should be using a directive. However, if you have the $event, you can get the raw element the event was triggered on easily:
$scope.clickHandler = function($event) {
var element = $event.target;
};
With a directive in angular it is easy to access the element. Just use the link function:
angular.module('myModule', [], function ($compileProvider) {
$compileProvider.directive('distortThatDiv', function distortThatDivDirective() {
return {
restrict: 'A',
link : function (scope, element, attrs) {
element.on('click', function () {
// do something
});
}
};
});
});
Your html would be:
<div ng-controller='myController'>
<a distort-that-div>My link</a>
</div>
Here's another alternative (not sure if this is a good practice, though):
In your template:
<div data-ng-click="foo()">Lorem ipsum</div>
In your controller:
angular.module('fooApp')
.controller('FooController', ['$scope', '$window', '$log', function FooController ($scope, $window, $log) {
$scope.foo = function foo () {
$log.info($window.currentTarget.innerHTML); // Outputs 'Lorem Ipsum';
}
}]);