I have a button on clicking which a popup is opened, addRow function is called to add a row. This row has a button on clicking which new rows can be added. However when i click on this, no function is called and nothing happens. Please help.
<body ng-app="myApp" ng-controller="myCtrl">
<div id="sample"><!-- onload="init()">-->
<h3>Make your own Flowchart</h3>
<button ng-click = "addSystem()" type="submit" id="btn-save">Add System</button>
Within my controller code starts as follows.
<script type="text/javascript">
var app=angular.module('myApp',[]);
app.controller('myCtrl', function($scope)
{
$scope.sysInfo=[];
later i have addSystem function is as follows:
$scope.addSystem=function()
{
console.log("Inside add system");
var str="<form><table id=\"sysTab\"></table></form>";
console.log("str is "+str);
$(str).dialog({
modal: true,
title:"Add system info",
height:"auto",
width:"auto",
buttons: {
'OK': function () {
//console.log($('#sysName'));
init();
$( this ).dialog( "close" );
},
'Cancel': function () {
$(this).dialog('close');
}
}
});
$scope.addRow();
}
$scope.addRow=function()
{
$scope.index=$scope.index+1;
console.log("index is "+$scope.index);
console.log("Inside add row");
$('#sysTab').append("<tr><td>System Name : </td><td><input rowid=\""+$scope.index+"\"type=\"text\" id=\"sysName"+$scope.index+"\"/></td><td><input type=\"text\" class=\"basic"+$scope.index+"\"/></td><td><input type=\"button\" value=\"+\" ng-click=\"addRow("+$scope.index+")\" title=\"Add new system info\"/></td><td><input type=\"button\" value=\"-\" onclick=\"deleteRow(this)\" title=\"Delete system info\"/><p id=\""+$scope.index+"\" style=\"display:none;\"></p></td></tr>");
console.log("row added");
$(".basic"+$scope.index).spectrum({
color: "#f00",
change: function(color) {
console.log("Inside change");
console.log($('#sysName'+$(this).closest('tr').index()).val());
sysInfo.push({"index":$(this).closest('tr').index(), "name": $('#sysName'+$(this).closest('tr').index()).val(), "color":color.toHexString()});
console.log(sysInfo);
}
});
}
i tried calling as $scope.addRow() but i am getting no response. i tried a lot without any success. Any pointers will be greatly helpful.
The issue of $scope not defined is resolved by explicitly calling out the scope as angular.element(document.getElementByID('master div element here').scope()
Related
I have a modal in Ionic that shows a list of country flags for the user to choose, however my ng-click on the language flag don't appear to fire the $scope.function() I have assigned. Here's what I've got:
Showing the modal:
$scope.showLanguages = function() {
var myPopup = $ionicPopup.show({
templateUrl: 'templates/languageSelect.html',
title: 'Language Select',
scope: $scope,
buttons: [
{
text: '<b>Close</b>',
type: 'button-positive',
onTap: function (e) {
return;
}
}
],
cssClass: 'animated bounceInDown'
});
}
My template that displays my flags, with the ng-click on them:
<div class="row">
<button ng-class="getFlagClass(language)" ng-click="setLanguage()" class="col flag-icon flag-icon-squared" ng-repeat="language in data.languages" />
</div>
And finally my ng-click function which is on the same scope as the one that opens the modal (notice the $scope being passed into the modal)
$scope.setLanguage = function() {
alert('test');
}
Can anyone suggest what I might be doing wrong here? This looks like a bug in Ionic but I could be wrong.
Thanks
It turns out it WAS working, but the alert wasn't being shown... I suspect this is because it was within a modal? I don't know.
Anyway, there's nothing wrong with the above code after all.
I am using this Plunker as a reference. I want to create a similar one however, the change is I want to add a button and on click each entry should be added in the list. I have created this Plunker. However, I need to add one by one item on click. How can I achieve that? Somehow, I am doing something wrong in this code.
$scope.addRow = function() {
$scope.source.push($scope.counter);
$scope.counter++;
}
Any help would be appreciated.
In order to do what you want, you need to call $scope.source.pageSize(page); after changing it, and then $scope.source.refresh() to apply this change to the source.
angular.module("app", ["kendo.directives"]).controller("MyCtrl", function ($scope) {
var page =1;
$scope.source = new kendo.data.DataSource({
transport: {
read: {
url: "http://demos.telerik.com/kendo-ui/service/products",
dataType: "jsonp"
}
},
pageSize: page
});
$scope.add= function(){
page++;
$scope.source.pageSize(page);
$scope.source.refresh();
}
});
here's working plnkr
You are just pushing the counter into the array, you should push the text instead.
angular.module("app", []).controller("MyCtrl", function ($scope) {
$scope.source = [];
$scope.addRow = function(text) {
$scope.source.push(text);
}
});
and in your view:
<div ng-app="app" ng-controller="MyCtrl">
<input type="text" ng-model="some_text"/>
<input type="submit" value="Add" ng-click="addRow(some_text)"/>
</div>
Take a look at this Plunker: http://plnkr.co/edit/m1gkM4Yp9xzAa09NDACK?p=preview
I'm trying to open a modal dialog at a location where the user clicked.
the modal dialog should open from a timer event, basically mouseDown start a timer, and mouseUp clear it, if enough time passed I want to open the dialog.
The dialog need to be opened at the location of the click.
I implement it all, but I do not know how to set the location of the dialog, I'm using angular bootstrap modal dialog.
any idea ?
here is a snippet of my code.
var timer = null;
var lastSceneSelectedLocation = { } ;
// handle / prepare for context menu event
$scope.mouseDown = function(event) {
lastSceneSelectedLocation.x = event.clientX;
lastSceneSelectedLocation.y = event.clientY;
timer = window.setTimeout($scope.openSceneMenu, 1000);
// tap and hold for 1 second to open menu
};
// cleanup context menu event
$scope.mouseUp = function() {
window.clearTimeout(timer);
};
$scope.ScenesContextOptions = ["Move Back", "Move Forward", "Duplicate", "Delete"];
$scope.SceneActionSelected = {};
// context menu event
$scope.openSceneMenu = function() {
console.log("in scene context menu");
var modalInstance = $modal.open({
templateUrl: 'views/SceneModalDialog.html',
scope: $scope,
windowClass: 'sceneContextMenu'
});
modalInstance.opened.then(function () {
console.log('modal opened');
});
modalInstance.result.then(function () {
console.log($scope.SceneActionSelected);
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
};
this maybe too late but here's how to use a angular-ui-bootstrap-popover with a custom template:
Note: you need do download a custom pull-request from angular-ui-bootstrap since the current version 0.12 doesn't support templates for popover hopefully this will be supported in 0.13.
then you can do something like this:
<button
class="btn btn-default"
popover-window-placement="top"
popover-window-trigger="click"
popover-window="templateb.html"
>
Popover With Template
</button>
notice: the directive is not popover anymore it's popover-window.
follow this issue for more information this plnkr shows a live example.
I think, bootstrap's modal is not a good idea for this taks, because you can't tell him position where to open. Maybe Popover will be more suited for your task?
A popover may satisfy your requirement.
https://angular-ui.github.io/bootstrap/#/popover
I modified the example Plunker from the Angular UI Bootstrap documentation to show how you could include a list of items to select from and then identify the selected item in your controller.
<div ng-controller="PopoverDemoCtrl">
<br/><br/><br/><br/><br/><br/>
<h4>Popover With Template</h4>
<button popover-template="dynamicPopover.templateUrl"
popover-title="{{dynamicPopover.title}}"
type="button"
class="btn btn-default">Popover With Template</button>
<script type="text/ng-template" id="myPopoverTemplate.html">
<ul>
<li ng-click="clickEvent(1)">Item 1</li>
<li ng-click="clickEvent(2)">Item 2</li>
<li ng-click="clickEvent(3)">Item 3</li>
</ul>
</script>
</div>
In your controller:
angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']);
angular.module('ui.bootstrap.demo').controller('PopoverDemoCtrl', function ($scope) {
$scope.dynamicPopover = {
templateUrl: 'myPopoverTemplate.html',
title: 'Popover With Template'
};
$scope.clickEvent = function (item) {
console.log("clickEvent: " + item);
alert("You Clicked Item: " + item);
};
});
Plunker
The Plunker is here:
I need some advice on refactoring a modal directive I have. I am just getting started with directives, so any other approach to my problem is welcome.
My program needs a confirmation modal, where we can confirm or cancel the desired action. It will appear in many places and needs to be able to have a programmable button. Cancel is consistent in that it will only hide the modal, the confirmation button needs to perform whatever action required.
I am currently using $rootScope to show / hide / configure the modal. Is this a bad idea? Please tell me.
This is what I am working with right now (roughly, as I have cut out a lot of the other unnecessary code):
index.html
<!doctype html>
<html lang="en">
<head>
<title>My App</title>
</head>
<body ng-controller="MenuCtrl">
<confirmmodal ng-show="$root.confirmModal.isVisible"></confirmmodal>
<ul>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
<div ng-view></div>
<!-- build:js scripts/main.js -->
<script data-main="scripts/main" src="lib/requirejs/require.js"></script>
<!-- endbuild -->
</body>
</html>
So my modal sits atop the ng-view and can be called from anywhere. It is inside a pseudo global controller, called MenuCtrl.
Here is the modal directive code:
directives.js
/* Confirm Modal */
.directive('confirmmodal', [function() {
return {
restrict: 'E',
templateUrl: 'view/templates/modal-confirm.tpl.html'
};
}])
It serves as a template for the following code:
modal-confirm.tpl.html
<!-- Confirm Modal Template -->
<div class="overlay">
<div class="overlay-content extended">
<span>{{$root.confirmModal.content}}</span>
<div class="buttons">
<button class="btn btn-default" ng-click="$root.confirmModal.secondary.action()">{{$root.confirmModal.secondary.content}}</button>
<button class="btn btn-primary" ng-click="$root.confirmModal.primary.action()">{{$root.confirmModal.primary.content}}</button>
</div>
</div>
</div>
I set a bunch of defaults in the app.run function:
app.js
app.run(['$rootScope', function ($rootScope) {
_.extend($rootScope, {
confirmModal: {
isVisible: false,
content: '',
primary: {
action: function() {
console.log('hello world');
},
content: 'Submit'
},
secondary: {
action: function() {
$rootScope.confirmModal.isVisible = false;
},
content: 'Cancel'
}
}
});
}]);
So I've also coded a modal trigger directive, the idea being that I can create different triggers that perform different actions with the modal.
directives.js
/* Resolve Event */
.directive('resolveevent', ['RequestService', '$location', function (RequestService, $location) {
return {
restrict: 'A',
scope: {
eventtype: '#',
eventid: '#',
index: '#'
},
controller: ['$scope', function($scope) {
$scope.remove = function(id) {
// remove the event from the events array
$scope.$parent.$parent.$parent.$parent.events.splice(id, 1);
},
$scope.config = function(config) {
_.extend($scope.$root.confirmModal, config);
},
$scope.isVisible = function() {
$scope.$apply(function() {
$scope.$root.confirmModal.isVisible = true;
});
}
}],
link: function( $scope, element, attrs ) {
var config = {
content: 'Are you sure you wish to resolve this event?',
primary: {
action: function() {
var config = {
url: '/Events/' + $scope.eventid,
method: 'PUT',
data: {
event_status: 'resolved'
},
cache: false
}
/* Update event with resolved status */
RequestService.makeApiRequest(config).success(function(response) {
$scope.$root.confirmModal.isVisible = false;
$scope.remove($scope.index);
});
},
content: 'Resolve Event'
}
}
element.on('click', function() {
if (!$scope.$root.confirmModal.isVisible) {
$scope.config(config);
$scope.isVisible();
}
});
}
}
}]);
And then I use a button on the view where my ng-repeat is found which is able to trigger the modal:
eventlist.html
<li ng-repeat="event in events">
<p>Event: {{ event.number }}</p>
<p>Group: {{ event.group_name }}</p>
<p>Record Date: {{ event.event_date | moment: 'MM/DD/YYYY h:mm A' }}</p>
<button resolveevent index="{{$index}}" eventid="{{ event.number }}" class="btn btn-default">Resolve</button>
</li>
This is what I've got, and it is working, however it seems like overkill, inefficient, and a nightmare to maintain. Can anyone chime in on a way to improve this? I appreciate any help, thanks in advance.
You can have a look at the bootstrap-ui project : http://angular-ui.github.io/bootstrap/
If you're using Bootstrap 3, be careful about the templates, and use the version without them. You can download bootstrap3 compliant templates here : https://github.com/angular-ui/bootstrap/tree/bootstrap3_bis2_modalPatch
A simple directive to confirm:
/**
* A generic confirmation for risky actions.
* Usage: Add attributes: ng-really-message="Really?" ng-really-click="takeAction()" function
*/
angular.module('app').directive('ngReallyClick', [function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
element.bind('click', function() {
var message = attrs.ngReallyMessage;
if (message && confirm(message)) {
scope.$apply(attrs.ngReallyClick);
}
});
}
}
}]);
My method might not be according to best practises, but I usually end up creating dedicated service that both has access to modal's scope and manipulates dom. Think of it as self injecting directive.
Here's the modal's container html (uses bootstrap's styling):
<div class="modal-backdrop"></div>
<div class="modal fade">
<div class="modal-dialog" ng-style="{width: width}">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" ng-click="close()" aria-hidden="true">×</button>
<h4 class="modal-title">{{title}}</h4>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button ng-repeat="(name, callback) in buttons" type="button" ng-click="callback()">{{name}}</button>
</div>
</div>
</div>
</div>
Then there's pseudo code of the DialogService:
.service('DialogService', function($compile, $http, $rootScope) {
this.open = function(options) {
//options contain various properties
//e.g. title, width, template or templateUrl, button map with callbacks
loadModalContainer()
.then(loadModalBody)
.then(init);
function init() {
modal = $('body').append(containerHtml).find('.modal');
modal.append(bodyHtml);
scope = (options.scope || $rootScope).$new();
if (options.controller) $controller(options.controller, {$scope: scope});
$compile(modal)(scope);
listenForEscKey();
}
function close() {
//clean up event listeners
//
if (options.onClose) options.onClose();
scope.$destroy();
$('body').find('.modal,.modal-backdrop').remove();
}
}
});
Of course, because of the async nature of the service, you have to implement some auto-close logic if second modal pops-up. From there is really easy, you can define concrete dialogs as separate services to abstract away the details:
.service('TermsModal', function(DialogService) {
this.open = function(acceptCallback, declineCallback, scope) {
DialogService.open({
templateUrl: '',
width: '',
buttons: {
accept: acceptCallback,
decline: declineCallback
},
scope: scope
});
}
})
Then from any controller you can open modal with an one-liner: TermsModal.open(acceptCallback, declineCallback, $scope)
There are several issues. First of all, it would be great to use transclusion, since now modal's child scope is littered with title, buttons, width properties.
Another thing is that I pass around modal body's width, but that's just my laziness (I cannot style bootstraps modal body width properly since it's hardcoded).
Also, I pass around local scopes from controllers because very often modal's body content is in one or another way related to the controller that invokes the modal. If, say, we have ItemController with item as scope property and we have an edit button to edit item's value in a modal, the child scope has to know about the model it's dealing with. So either it's passing around scope or passing needed values directly in options. I prefer scope because that gives more flexibility and with child scope intialization it is really hard to mess up orginal model.
All in all, the power and flexibility this set-up gives justifies the fact that service is messing a bit with the DOM. Your rootScope becomes free of global state (the service manages its own state without giving details to the outside world) and your main template is free of modal partials/directives/whatever that may or may not be used.
I have created a small confirmation directive which, opens a modal and executes the code you want, if the modal is confirmed:
app.html
<button type="button" class="btn btn-default"
nait-confirm-click
confirm="Do you really want to remove this record?"
confirm-if="user.disabled == true"
do="remove(user)">
Remove
</button>
script.js
angular
.module('xyz', ['ui.bootstrap'])
.directive('naitConfirmClick', function($modal, $parse) {
return {
restrict: 'EA',
link: function(scope, element, attrs) {
if (!attrs.do) {
return;
}
// register the confirmation event
var confirmButtonText = attrs.confirmButtonText ? attrs.confirmButtonText : 'OK';
var cancelButtonText = attrs.cancelButtonText ? attrs.cancelButtonText : 'Cancel';
element.click(function() {
// action that should be executed if user confirms
var doThis = $parse(attrs.do);
// condition for confirmation
if (attrs.confirmIf) {
var confirmationCondition = $parse(attrs.confirmIf);
if (!confirmationCondition(scope)) {
// if no confirmation is needed, we can execute the action and leave
doThis(scope);
scope.$apply();
return;
}
}
$modal
.open({
template: '<div class="modal-body">' + attrs.confirm + '</div>'
+ '<div class="modal-footer">'
+ '<button type="button" class="btn btn-default btn-naitsirch-confirm pull-right" ng-click="$close(\'ok\')">' + confirmButtonText + '</button>'
+ '<button type="button" class="btn btn-default btn-naitsirch-cancel pull-right" ng-click="$dismiss(\'cancel\')">' + cancelButtonText + '</button>'
+ '</div>'
})
.result.then(function() {
doThis(scope);
scope.$apply()
})
;
});
}
};
})
;
Now, if you click on the button with the nait-confirm-click it opens a modal with two buttons and the text you have passed by the confirm attribute. If you click the cancel button, nothing will happen. If you confirm by clicking "OK", the expression, which you have passed by the do attribute, will be executed.
If you pass an expression in the optional confirm-if attribute, the modal will only be opened if the expression is true. If the expression is false, the action will be executed without asking.
I hope this snippet will help someone ;)
I have a simple list of items. I want to be able to scroll to the bottom of the element displaying the items whenever I add more items. I understood there is no way of hooking to the end of the $apply() function, so what might be my solution?
Here is a jsfiddle to illustrate my problem. after adding enough items, the ul element doesn't scroll to the bottom...
There's the very awesome angularjs-scroll-glue available, which does exactly what you want.
All you need to do is to apply the scroll-glue directive to your container element, and you get exactly what you're looking for.
There's also a demo available.
Another valid solution to this is using $timeout. Using a timeout value of 0, angular will wait until the DOM is rendered before calling the function you pass to $timeout. So, after you add an element to the list, you can use this to wait for your new element to be added to the DOM before scrolling to the bottom.
Like #Mark Coleman's solution, this won't require any extra external libraries.
var myApp = angular.module('myApp', []);
function MyCtrl($scope, $timeout) {
$scope.list = ["item 1", "item 2", "item 3", "item 4", "item 5"];
$scope.add = function() {
$scope.list.push("new item");
$timeout(function() {
var scroller = document.getElementById("autoscroll");
scroller.scrollTop = scroller.scrollHeight;
}, 0, false);
}
}
ul {
height: 150px;
overflow: scroll;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<button ng-click="add()">Add</button>
<ul id="autoscroll">
<li ng-repeat="item in list">{{item}}</li>
</ul>
</div>
</div>
A simple working example (no need for plugins or directives)...
.controller('Controller', function($scope, $anchorScroll, $location, $timeout) {
$scope.func = function(data) {
// some data appending here...
$timeout(function() {
$location.hash('end');
$anchorScroll();
})
}
})
The trick that did it for me was wrapping the anchorScroll command with $timeout, that way the scope was resolved and it automatically shifted to an element at the end of the page.
You could create a simple directive that bind a click handler that scrolls the <ul> to the bottom each time.
myApp.directive("scrollBottom", function(){
return {
link: function(scope, element, attr){
var $id= $("#" + attr.scrollBottom);
$(element).on("click", function(){
$id.scrollTop($id[0].scrollHeight);
});
}
}
});
example on jsfiddle
You can use the AnchorScroll.. here the documentation: https://docs.angularjs.org/api/ng/service/$anchorScroll
You can achieve this using angularjs custom directory.
example :
<ul style="overflow: auto; max-height: 160px;" id="promptAnswerBlock">
<li ng-repeat="obj in objectKist track by $index" on-finish-render="ngRepeatFinished">
app.directive('onFinishRender', function($timeout) {
return {
restrict : 'A',
link : function(scope, element, attr) {
if (scope.$last === true) {
$timeout(function() {
$('#promptAnswerBlock').scrollTop($('#promptAnswerBlock')[0].scrollHeight + 150);
});
}
}
}
});
</li>